diff --git a/CREDITS-wolfcam.txt b/CREDITS-wolfcam.txt index bf03cdf..5eb1b81 100644 --- a/CREDITS-wolfcam.txt +++ b/CREDITS-wolfcam.txt @@ -49,5 +49,10 @@ Maverick and quake live developers for information regarding dm90 and dm91 proto Dmitry 'qrealka' Loginov: cpma pro-sound description Ravensoft: png output from Jedi Academy GNU Unifont: font files -Gian 'myT' Schellenbaum: information regarding cpma 1.5[01] changes +Gian 'myT' Schellenbaum: information regarding cpma 1.5[01] changes, older demo protocol information from Uber Demo Tools Jaco Kroon: backtrace code +Thilo Shulz and Zack Middleton: information about older Quake 3 protocols + + https://github.com/zturtleman/lilium-arena-classic + + "Credit for Quake 3 protocol 43 goes to Thilo Schulz (for ioEF) and Zack Middleton." diff --git a/FIXME b/FIXME index f6d8c0a..21c30c8 100644 --- a/FIXME +++ b/FIXME @@ -1836,7 +1836,6 @@ No locals. * ^ flag icon more visible? * overflow player name in end game stats : engame-stats-player-name-overflow.dm_91 * zoom 2 pov stuck zoom-stuck-2-57.dm_91 -- 2016-07-31 seems to be a fov script, can't fix -* confusion between cgs.protocol and cgs.realProtocol * /at now+3sec (offset for 'now' and also option to run only once) * option for /wait with exact time instead of frame time * q3mme command time based demo smoothing @@ -2430,6 +2429,111 @@ Q_GetCpFromUtf8 invalid number of bytes specified in utf8 character 255 * VM crash with .wolfcamql/baseq3/z-cpma-pak153b2.pk3 +* demo protocol 43-48 + + 43 working + 44 no demos + 45 working -- treated as 43 + 46 working + 47 + 48 working + + - protocol 46 double check CS_LOCATIONS with team demo + - protocol 45 /record + - protocol 45 voip + - protocol 46 voip + - protocol 45 streaming + - protocol 46 streaming + - protocol 45 check PERS_ + + quake3 1.25 change log says: Two new medals will be awarded to players during Capture the Flag games. These medals recognize individual player actions that often go unheralded in the heat of play. + + - protocol 46 + + - protocol 46 scores SCSIZE = 15, what is the last one? + + - protocol 46 check PERS_CAPTURES + + - protocol 45 /share/tmp/si_* CS_WARMUP not -1 during warmup before ready -- shows big number countdown + + - client connect in demo parsing with protocol 43 might not be correct + + - multiple demos : /demo duel.dm_48 old12.dm_48 + + duel.dm_48 old18.dm_48 + + (warmup) 62:02 processsnapshots() couldn't get nextsnap 3506 +(warmup) 62:02 processsnapshots() couldn't get nextsnap 3506 +(warmup) 62:02 processsnapshots() couldn't get nextsnap 3507 +(warmup) 62:02 processsnapshots() couldn't get nextsnap 3507 +(warmup) 62:02 processsnapshots() couldn't get nextsnap 3507 +(warmup) 62:02 processsnapshots() couldn't get nextsnap 3508 +(warmup) 62:02 processsnapshots() couldn't get nextsnap 3508 + + - CL_ParseExtraServerMessage not checking protocol correctly + + - recording + + * tested protocol 48 output in quake3 127 and it seems to be working + + - option to force protocol + - impact of not having client number in demo messages + - unknown command viewlist - is that just osp? -- 2024-08-23 yes + + - test /share/tmp/demo000.dm_48 and /share/tmp/demo001.dm_48 + + - don't check com_protocol in msg.c + - /streamdemo check protocol 43 + - protocol 43 /players no main client number in demos? + - tdm.dm3 protocol 43 no end of game stats (Timelimit hit not checked correctly?) + + - speex voip + - /streamdemo spamming: + +Opening IP socket: 0.0.0.0:27960 +disconnect +checking workshops +hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh +CL_ParseExtraServerMessage: Illegible server message 117 for demoFile 0CL_ParseExtraServerMessage: Illegible server message 193 for demoFile 0CL_ParseExtraServerMessage: Illegible server message 203 for demoFile 0CL_ParseExtraServerMessage: Illegible server message 193 for demoFile 0CL_ParseExtraServerMessage: Illegible server message 186 for demoFile + + + - why doesn't stream_demo_look_ahead() get full message? + +stream_demo_look_ahead() waiting for message, need 16, demo position 0 +Com_Error(): CL_ParseServerMessage: Illegible server message 117******************** + +acano@debian:/share/tmp/stream1$ python slow-copy.py duel.dm_48 x.dm_48 +3800 +len: 3800, position 3800 +100 +len: 100, position 3900 +^[OS^[OS^[OS^[OS^CTraceback (most recent call last): + File "/share/tmp/stream1/slow-copy.py", line 13, in + numKBstr = input() + ^^^^^^^ +KeyboardInterrupt + +* stream_demo_look_ahead() why was that added? + + +* //FIXME 2024-07-28 this is a mess, should have commented why it was needed, the stream can have data written after this returns and what this would have detected as not enough data for message might have it afterwards, that could mess up the snap/message count, for something like that maybe only read the demo/stream end position once + // hereeee1111111 + stream_demo_look_ahead(); + +changeset: 9050:074cb4fb2345 +user: acano +date: Thu Oct 21 06:22:18 2021 -0400 +summary: demo streaming look ahead to allow fast forwarding + + +* /streamdemo can't quit (f4 ) while in checking for workshop screen + +* check demo streaming and /record if you don't even have gamestate yet + +* multiple demos with smaller second one: + + CL_ReadExtraDemoMessage demo file 2 ended spammed if second is shorter and nothing to break outer loop? + ---- diff --git a/Makefile.local b/Makefile.local index 0de6c23..1d55f53 100644 --- a/Makefile.local +++ b/Makefile.local @@ -1,4 +1,4 @@ -VERSION=12.6 +VERSION=12.7test1 USE_CODEC_VORBIS=1 USE_FREETYPE=1 diff --git a/code/cgame/cg_consolecmds.c b/code/cgame/cg_consolecmds.c index ef0d129..052bdeb 100644 --- a/code/cgame/cg_consolecmds.c +++ b/code/cgame/cg_consolecmds.c @@ -1270,7 +1270,7 @@ static void CG_ListEntities_f (void) ent = ¢->currentState; if (ent->eType < 0) { etypeStr = "invalid"; - } else if (ent->eType >= ET_EVENTS) { + } else if ((cgs.realProtocol >= 46 && ent->eType >= ET_EVENTS) || (cgs.realProtocol < 46 && ent->eType >= (ET_EVENTS - 1))) { etypeStr = "event"; } else { etypeStr = entTypes[ent->eType]; @@ -6382,7 +6382,7 @@ static void CG_ClientOverride_f (void) } else { //Com_Printf("change config string: '%s' '%s' '%s'\n", key, value, buffer); Info_SetValueForKey(buffer, key, value); - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { CG_ChangeConfigString(buffer, CSQ3_PLAYERS + j); } else { CG_ChangeConfigString(buffer, CS_PLAYERS + j); @@ -8035,7 +8035,7 @@ static void CG_SeekNextRound_f (void) if (roundStartIndex > -1) { int roundTime; - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { roundTime = atoi(CG_ConfigString(CS_ROUND_TIME)); // ql specific diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c index c392a25..44278c6 100644 --- a/code/cgame/cg_draw.c +++ b/code/cgame/cg_draw.c @@ -7065,7 +7065,7 @@ static void CG_RoundAnnouncements (void) #if 0 //FIXME cpma and others... - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } #endif @@ -7200,7 +7200,7 @@ static void CG_DrawCenter (void) return; } else { //FIXME dont use centerprint it will hover around after match has restarted - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { CG_CenterPrint(va("Match resumes in ^5%d ^7seconds", (cgs.timeoutEndTime - cg.time) / 1000 + 1), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH); //if (cgs.timeoutEndTime - cg.snap->serverTime == 0) { if (cgs.timeoutEndTime - cg.time == 0) { @@ -7213,7 +7213,7 @@ static void CG_DrawCenter (void) } if (cgs.timeoutCountingDown) { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { // clear message CG_CenterPrint("", SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH); } @@ -10127,7 +10127,7 @@ static void CG_DrawWarmup( void ) { x = cg_drawWaitingForPlayersX.value; y = cg_drawWaitingForPlayersY.value; - if (cgs.protocol == PROTOCOL_QL && *CG_ConfigString(CS_READY_UP_TIME)) { + if (cgs.protocolClass == PROTOCOL_QL && *CG_ConfigString(CS_READY_UP_TIME)) { readyUpTime = atoi(CG_ConfigString(CS_READY_UP_TIME)); if (readyUpTime > cg.time) { int nx; diff --git a/code/cgame/cg_ents.c b/code/cgame/cg_ents.c index 023ea08..db69063 100644 --- a/code/cgame/cg_ents.c +++ b/code/cgame/cg_ents.c @@ -205,7 +205,7 @@ static void CG_EntityEffects( const centity_t *cent ) { } } else if (cent->currentState.loopSound) { - //Com_Printf("%d loop %d '%s'\n", cent->currentState.number, cent->currentState.loopSound, CG_ConfigString(CS_SOUNDS + cent->currentState.loopSound - (cgs.protocol == PROTOCOL_QL ? 1 : 0))); + //Com_Printf("%d loop %d '%s'\n", cent->currentState.number, cent->currentState.loopSound, CG_ConfigString(CS_SOUNDS + cent->currentState.loopSound - (cgs.protocolClass == PROTOCOL_QL ? 1 : 0))); if (cent->currentState.eType != ET_SPEAKER) { //Com_Printf("^3adding loop sound to %d %d %s\n", cent->currentState.number, cent->currentState.loopSound, CG_ConfigString( CS_SOUNDS + cent->currentState.loopSound)); @@ -1077,7 +1077,7 @@ static void CG_TieredArmorAvailability (const centity_t *cent, const gitem_t *it return; } - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } @@ -1150,7 +1150,7 @@ static void CG_DrawTimerPie (const centity_t *cent) qhandle_t currentShader; int num5Chunks; - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } @@ -1299,7 +1299,7 @@ static void CG_DrawPowerupRespawnPOI (const centity_t *cent) vec3_t org; float minWidth, maxWidth, radius, dist; - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } @@ -1393,7 +1393,7 @@ static void CG_DrawPowerupAvailable (const centity_t *cent) float frac; float total; - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } @@ -1944,7 +1944,7 @@ static void CG_Missile( centity_t *cent ) { clientNum = s1->clientNum; } - if (cgs.protocol == PROTOCOL_QL && (clientNum >= 0 && clientNum < MAX_CLIENTS)) { + if (cgs.protocolClass == PROTOCOL_QL && (clientNum >= 0 && clientNum < MAX_CLIENTS)) { CG_CopyPlayerDataToScriptData(&cg_entities[clientNum]); } else { ScriptVars.inEyes = qfalse; @@ -3251,7 +3251,7 @@ static void CG_AddCEntity( centity_t *cent ) { int entNum; // event-only entities will have been dealt with already - if ( cent->currentState.eType >= ET_EVENTS ) { + if ( (cgs.realProtocol >= 46 && cent->currentState.eType >= ET_EVENTS) || (cgs.realProtocol < 46 && cent->currentState.eType >= (ET_EVENTS - 1)) ) { return; } diff --git a/code/cgame/cg_event.c b/code/cgame/cg_event.c index 1b2ad1b..ef82ee4 100644 --- a/code/cgame/cg_event.c +++ b/code/cgame/cg_event.c @@ -195,7 +195,7 @@ static void CG_Obituary( const entityState_t *ent ) { //if (target == cg.snap->ps.clientNum && cg.clientNum == cg.snap->ps.clientNum) { if (target == cg.clientNum) { if (cgs.gametype == GT_CA || cgs.gametype == GT_CTFS) { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int roundTime; roundTime = atoi(CG_ConfigString(CS_ROUND_TIME)); @@ -1157,6 +1157,8 @@ static int CG_EntityEventFromQ3 (int event) { int e; + //Com_Printf(" event in: %d\n", event); + if (cgs.cpma && event >= EVQ3_PROXIMITY_MINE_STICK) { switch (event) { case EVQ3_PROXIMITY_MINE_STICK: @@ -1172,265 +1174,534 @@ static int CG_EntityEventFromQ3 (int event) e = -1; - switch (event) { - case EVQ3_NONE: - e = EV_NONE; - break; - case EVQ3_FOOTSTEP: - e = EV_FOOTSTEP; - break; - case EVQ3_FOOTSTEP_METAL: - e = EV_FOOTSTEP_METAL; - break; - case EVQ3_FOOTSPLASH: - e = EV_FOOTSPLASH; - break; - case EVQ3_FOOTWADE: - e = EV_FOOTWADE; - break; - case EVQ3_SWIM: - e = EV_SWIM; - break; - case EVQ3_STEP_4: - e = EV_STEP_4; - break; - case EVQ3_STEP_8: - e = EV_STEP_8; - break; - case EVQ3_STEP_12: - e = EV_STEP_12; - break; - case EVQ3_STEP_16: - e = EV_STEP_16; - break; - case EVQ3_FALL_SHORT: - e = EV_FALL_SHORT; - break; - case EVQ3_FALL_MEDIUM: - e = EV_FALL_MEDIUM; - break; - case EVQ3_FALL_FAR: - e = EV_FALL_FAR; - break; - case EVQ3_JUMP_PAD: // boing sound at origin, jump sound on player - e = EV_JUMP_PAD; - break; - case EVQ3_JUMP: - e = EV_JUMP; - break; - case EVQ3_WATER_TOUCH: // foot touches - e = EV_WATER_TOUCH; - break; - case EVQ3_WATER_LEAVE: // foot leaves - e = EV_WATER_LEAVE; - break; - case EVQ3_WATER_UNDER: // head touches - e = EV_WATER_UNDER; - break; - case EVQ3_WATER_CLEAR: // head leaves - e = EV_WATER_CLEAR; - break; - case EVQ3_ITEM_PICKUP: // normal item pickups are predictable - e = EV_ITEM_PICKUP; - break; - case EVQ3_GLOBAL_ITEM_PICKUP: // powerup / team sounds are broadcast to everyone - e = EV_GLOBAL_ITEM_PICKUP; - break; - case EVQ3_NOAMMO: - e = EV_NOAMMO; - break; - case EVQ3_CHANGE_WEAPON: - e = EV_CHANGE_WEAPON; - break; - case EVQ3_FIRE_WEAPON: - e = EV_FIRE_WEAPON; - break; - case EVQ3_USE_ITEM0: - e = EV_USE_ITEM0; - break; - case EVQ3_USE_ITEM1: - e = EV_USE_ITEM1; - break; - case EVQ3_USE_ITEM2: - e = EV_USE_ITEM2; - break; - case EVQ3_USE_ITEM3: - e = EV_USE_ITEM3; - break; - case EVQ3_USE_ITEM4: - e = EV_USE_ITEM4; - break; - case EVQ3_USE_ITEM5: - e = EV_USE_ITEM5; - break; - case EVQ3_USE_ITEM6: - e = EV_USE_ITEM6; - break; - case EVQ3_USE_ITEM7: - e = EV_USE_ITEM7; - break; - case EVQ3_USE_ITEM8: - e = EV_USE_ITEM8; - break; - case EVQ3_USE_ITEM9: - e = EV_USE_ITEM9; - break; - case EVQ3_USE_ITEM10: - e = EV_USE_ITEM10; - break; - case EVQ3_USE_ITEM11: - e = EV_USE_ITEM11; - break; - case EVQ3_USE_ITEM12: - e = EV_USE_ITEM12; - break; - case EVQ3_USE_ITEM13: - e = EV_USE_ITEM13; - break; - case EVQ3_USE_ITEM14: - e = EV_USE_ITEM14; - break; - case EVQ3_USE_ITEM15: - e = EV_USE_ITEM15; - break; - case EVQ3_ITEM_RESPAWN: - e = EV_ITEM_RESPAWN; - break; - case EVQ3_ITEM_POP: - e = EV_ITEM_POP; - break; - case EVQ3_PLAYER_TELEPORT_IN: - e = EV_PLAYER_TELEPORT_IN; - break; - case EVQ3_PLAYER_TELEPORT_OUT: - e = EV_PLAYER_TELEPORT_OUT; - break; - case EVQ3_GRENADE_BOUNCE: // eventParm will be the soundindex - e = EV_GRENADE_BOUNCE; - break; - case EVQ3_GENERAL_SOUND: - e = EV_GENERAL_SOUND; - break; - case EVQ3_GLOBAL_SOUND: // no attenuation - e = EV_GLOBAL_SOUND; - break; - case EVQ3_GLOBAL_TEAM_SOUND: - e = EV_GLOBAL_TEAM_SOUND; - break; - case EVQ3_BULLET_HIT_FLESH: - e = EV_BULLET_HIT_FLESH; - break; - case EVQ3_BULLET_HIT_WALL: - e = EV_BULLET_HIT_WALL; - break; - case EVQ3_MISSILE_HIT: - e = EV_MISSILE_HIT; - break; - case EVQ3_MISSILE_MISS: - e = EV_MISSILE_MISS; - break; - case EVQ3_MISSILE_MISS_METAL: - e = EV_MISSILE_MISS_METAL; - break; - case EVQ3_RAILTRAIL: - e = EV_RAILTRAIL; - break; - case EVQ3_SHOTGUN: - e = EV_SHOTGUN; - break; - case EVQ3_BULLET: // otherEntity is the shooter - Com_Printf("FIXME q3 EV_BULLET\n"); - //e = EV_BULLET; - e = EV_BULLET_HIT_FLESH; - break; - case EVQ3_PAIN: - e = EV_PAIN; - break; - case EVQ3_DEATH1: - e = EV_DEATH1; - break; - case EVQ3_DEATH2: - e = EV_DEATH2; - break; - case EVQ3_DEATH3: - e = EV_DEATH3; - break; - case EVQ3_OBITUARY: - e = EV_OBITUARY; - break; - case EVQ3_POWERUP_QUAD: - e = EV_POWERUP_QUAD; - break; - case EVQ3_POWERUP_BATTLESUIT: - e = EV_POWERUP_BATTLESUIT; - break; - case EVQ3_POWERUP_REGEN: - e = EV_POWERUP_REGEN; - break; - case EVQ3_GIB_PLAYER: // gib a previously living player - e = EV_GIB_PLAYER; - break; - case EVQ3_SCOREPLUM: // score plum - e = EV_SCOREPLUM; - break; -//#ifdef MISSIONPACK - case EVQ3_PROXIMITY_MINE_STICK: - e = EV_PROXIMITY_MINE_STICK ; - break; - case EVQ3_PROXIMITY_MINE_TRIGGER: - e = EV_PROXIMITY_MINE_TRIGGER ; - break; - case EVQ3_KAMIKAZE: // kamikaze explodes - e = EV_KAMIKAZE; - break; - case EVQ3_OBELISKEXPLODE: // obelisk explodes - e = EV_OBELISKEXPLODE; - break; - case EVQ3_OBELISKPAIN: // obelisk is in pain - e = EV_OBELISKPAIN; - break; - case EVQ3_INVUL_IMPACT: // invulnerability sphere impact - e = EV_INVUL_IMPACT; - break; - case EVQ3_JUICED: // invulnerability juiced effect - e = EV_JUICED; - break; - case EVQ3_LIGHTNINGBOLT: // lightning bolt bounced of invulnerability sphere - e = EV_LIGHTNINGBOLT; - break; -//#endif - case EVQ3_DEBUG_LINE: - e = EV_DEBUG_LINE; - break; - case EVQ3_STOPLOOPINGSOUND: - e = EV_STOPLOOPINGSOUND; - break; - case EVQ3_TAUNT: - e = EV_TAUNT; - break; - case EVQ3_TAUNT_YES: - e = EV_TAUNT_YES; - break; - case EVQ3_TAUNT_NO: - e = EV_TAUNT_NO; - break; - case EVQ3_TAUNT_FOLLOWME: - e = EV_TAUNT_FOLLOWME; - break; - case EVQ3_TAUNT_GETFLAG: - e = EV_TAUNT_GETFLAG; - break; - case EVQ3_TAUNT_GUARDBASE: - e = EV_TAUNT_GUARDBASE; - break; - case EVQ3_TAUNT_PATROL: - e = EV_TAUNT_PATROL; - break; + if (cgs.realProtocol < 46) { + switch (event) { + case EVQ3DM3_NONE: + e = EV_NONE; + break; + case EVQ3DM3_FOOTSTEP: + e = EV_FOOTSTEP; + break; + case EVQ3DM3_FOOTSTEP_METAL: + e = EV_FOOTSTEP_METAL; + break; + case EVQ3DM3_FOOTSPLASH: + e = EV_FOOTSPLASH; + break; + case EVQ3DM3_FOOTWADE: + e = EV_FOOTWADE; + break; + case EVQ3DM3_SWIM: + e = EV_SWIM; + break; + case EVQ3DM3_STEP_4: + e = EV_STEP_4; + break; + case EVQ3DM3_STEP_8: + e = EV_STEP_8; + break; + case EVQ3DM3_STEP_12: + e = EV_STEP_12; + break; + case EVQ3DM3_STEP_16: + e = EV_STEP_16; + break; + case EVQ3DM3_FALL_SHORT: + e = EV_FALL_SHORT; + break; + case EVQ3DM3_FALL_MEDIUM: + e = EV_FALL_MEDIUM; + break; + case EVQ3DM3_FALL_FAR: + e = EV_FALL_FAR; + break; + case EVQ3DM3_JUMP_PAD: // boing sound at origin, jump sound on player + e = EV_JUMP_PAD; + break; + case EVQ3DM3_JUMP: + e = EV_JUMP; + break; + case EVQ3DM3_WATER_TOUCH: // foot touches + e = EV_WATER_TOUCH; + break; + case EVQ3DM3_WATER_LEAVE: // foot leaves + e = EV_WATER_LEAVE; + break; + case EVQ3DM3_WATER_UNDER: // head touches + e = EV_WATER_UNDER; + break; + case EVQ3DM3_WATER_CLEAR: // head leaves + e = EV_WATER_CLEAR; + break; + case EVQ3DM3_ITEM_PICKUP: // normal item pickups are predictable + e = EV_ITEM_PICKUP; + break; + case EVQ3DM3_GLOBAL_ITEM_PICKUP: // powerup / team sounds are broadcast to everyone + e = EV_GLOBAL_ITEM_PICKUP; + break; + case EVQ3DM3_NOAMMO: + e = EV_NOAMMO; + break; + case EVQ3DM3_CHANGE_WEAPON: + e = EV_CHANGE_WEAPON; + break; + case EVQ3DM3_FIRE_WEAPON: + e = EV_FIRE_WEAPON; + break; + case EVQ3DM3_USE_ITEM0: + e = EV_USE_ITEM0; + break; + case EVQ3DM3_USE_ITEM1: + e = EV_USE_ITEM1; + break; + case EVQ3DM3_USE_ITEM2: + e = EV_USE_ITEM2; + break; + case EVQ3DM3_USE_ITEM3: + e = EV_USE_ITEM3; + break; + case EVQ3DM3_USE_ITEM4: + e = EV_USE_ITEM4; + break; + case EVQ3DM3_USE_ITEM5: + e = EV_USE_ITEM5; + break; + case EVQ3DM3_USE_ITEM6: + e = EV_USE_ITEM6; + break; + case EVQ3DM3_USE_ITEM7: + e = EV_USE_ITEM7; + break; + case EVQ3DM3_USE_ITEM8: + e = EV_USE_ITEM8; + break; + case EVQ3DM3_USE_ITEM9: + e = EV_USE_ITEM9; + break; + case EVQ3DM3_USE_ITEM10: + e = EV_USE_ITEM10; + break; + case EVQ3DM3_USE_ITEM11: + e = EV_USE_ITEM11; + break; + case EVQ3DM3_USE_ITEM12: + e = EV_USE_ITEM12; + break; + case EVQ3DM3_USE_ITEM13: + e = EV_USE_ITEM13; + break; + case EVQ3DM3_USE_ITEM14: + e = EV_USE_ITEM14; + break; + case EVQ3DM3_USE_ITEM15: + e = EV_USE_ITEM15; + break; + case EVQ3DM3_ITEM_RESPAWN: + e = EV_ITEM_RESPAWN; + break; + case EVQ3DM3_ITEM_POP: + e = EV_ITEM_POP; + break; + case EVQ3DM3_PLAYER_TELEPORT_IN: + e = EV_PLAYER_TELEPORT_IN; + break; + case EVQ3DM3_PLAYER_TELEPORT_OUT: + e = EV_PLAYER_TELEPORT_OUT; + break; + case EVQ3DM3_GRENADE_BOUNCE: // eventParm will be the soundindex + e = EV_GRENADE_BOUNCE; + break; + case EVQ3DM3_GENERAL_SOUND: + e = EV_GENERAL_SOUND; + break; + case EVQ3DM3_GLOBAL_SOUND: // no attenuation + e = EV_GLOBAL_SOUND; + break; - default: - Com_Printf("unknown q3 event %d\n", event); - e = event; - break; + // not in protocol 43 + //case EVQ3DM3_GLOBAL_TEAM_SOUND: + // e = EV_GLOBAL_TEAM_SOUND; + // break; + + case EVQ3DM3_BULLET_HIT_FLESH: + e = EV_BULLET_HIT_FLESH; + break; + case EVQ3DM3_BULLET_HIT_WALL: + e = EV_BULLET_HIT_WALL; + break; + case EVQ3DM3_MISSILE_HIT: + e = EV_MISSILE_HIT; + break; + case EVQ3DM3_MISSILE_MISS: + e = EV_MISSILE_MISS; + break; + + // not in protocol 43 + //case EVQ3DM3_MISSILE_MISS_METAL: + // e = EV_MISSILE_MISS_METAL; + // break; + + case EVQ3DM3_RAILTRAIL: + e = EV_RAILTRAIL; + break; + case EVQ3DM3_SHOTGUN: + e = EV_SHOTGUN; + break; + case EVQ3DM3_BULLET: // otherEntity is the shooter + Com_Printf("FIXME q3 EV_BULLET\n"); + //e = EV_BULLET; + e = EV_BULLET_HIT_FLESH; + break; + case EVQ3DM3_PAIN: + e = EV_PAIN; + break; + case EVQ3DM3_DEATH1: + e = EV_DEATH1; + break; + case EVQ3DM3_DEATH2: + e = EV_DEATH2; + break; + case EVQ3DM3_DEATH3: + e = EV_DEATH3; + break; + case EVQ3DM3_OBITUARY: + e = EV_OBITUARY; + break; + case EVQ3DM3_POWERUP_QUAD: + e = EV_POWERUP_QUAD; + break; + case EVQ3DM3_POWERUP_BATTLESUIT: + e = EV_POWERUP_BATTLESUIT; + break; + case EVQ3DM3_POWERUP_REGEN: + e = EV_POWERUP_REGEN; + break; + case EVQ3DM3_GIB_PLAYER: // gib a previously living player + e = EV_GIB_PLAYER; + break; + case EVQ3DM3_SCOREPLUM: // score plum + e = EV_SCOREPLUM; + break; + //#ifdef MISSIONPACK + case EVQ3DM3_PROXIMITY_MINE_STICK: + e = EV_PROXIMITY_MINE_STICK ; + break; + case EVQ3DM3_PROXIMITY_MINE_TRIGGER: + e = EV_PROXIMITY_MINE_TRIGGER ; + break; + case EVQ3DM3_KAMIKAZE: // kamikaze explodes + e = EV_KAMIKAZE; + break; + case EVQ3DM3_OBELISKEXPLODE: // obelisk explodes + e = EV_OBELISKEXPLODE; + break; + case EVQ3DM3_OBELISKPAIN: // obelisk is in pain + e = EV_OBELISKPAIN; + break; + case EVQ3DM3_INVUL_IMPACT: // invulnerability sphere impact + e = EV_INVUL_IMPACT; + break; + case EVQ3DM3_JUICED: // invulnerability juiced effect + e = EV_JUICED; + break; + case EVQ3DM3_LIGHTNINGBOLT: // lightning bolt bounced of invulnerability sphere + e = EV_LIGHTNINGBOLT; + break; + //#endif + case EVQ3DM3_DEBUG_LINE: + e = EV_DEBUG_LINE; + break; + case EVQ3DM3_STOPLOOPINGSOUND: + e = EV_STOPLOOPINGSOUND; + break; + case EVQ3DM3_TAUNT: + e = EV_TAUNT; + break; + case EVQ3DM3_TAUNT_YES: + e = EV_TAUNT_YES; + break; + case EVQ3DM3_TAUNT_NO: + e = EV_TAUNT_NO; + break; + case EVQ3DM3_TAUNT_FOLLOWME: + e = EV_TAUNT_FOLLOWME; + break; + case EVQ3DM3_TAUNT_GETFLAG: + e = EV_TAUNT_GETFLAG; + break; + case EVQ3DM3_TAUNT_GUARDBASE: + e = EV_TAUNT_GUARDBASE; + break; + case EVQ3DM3_TAUNT_PATROL: + e = EV_TAUNT_PATROL; + break; + + default: + Com_Printf("unknown q3 dm3 event %d\n", event); + e = event; + break; + } + } else { + switch (event) { + case EVQ3_NONE: + e = EV_NONE; + break; + case EVQ3_FOOTSTEP: + e = EV_FOOTSTEP; + break; + case EVQ3_FOOTSTEP_METAL: + e = EV_FOOTSTEP_METAL; + break; + case EVQ3_FOOTSPLASH: + e = EV_FOOTSPLASH; + break; + case EVQ3_FOOTWADE: + e = EV_FOOTWADE; + break; + case EVQ3_SWIM: + e = EV_SWIM; + break; + case EVQ3_STEP_4: + e = EV_STEP_4; + break; + case EVQ3_STEP_8: + e = EV_STEP_8; + break; + case EVQ3_STEP_12: + e = EV_STEP_12; + break; + case EVQ3_STEP_16: + e = EV_STEP_16; + break; + case EVQ3_FALL_SHORT: + e = EV_FALL_SHORT; + break; + case EVQ3_FALL_MEDIUM: + e = EV_FALL_MEDIUM; + break; + case EVQ3_FALL_FAR: + e = EV_FALL_FAR; + break; + case EVQ3_JUMP_PAD: // boing sound at origin, jump sound on player + e = EV_JUMP_PAD; + break; + case EVQ3_JUMP: + e = EV_JUMP; + break; + case EVQ3_WATER_TOUCH: // foot touches + e = EV_WATER_TOUCH; + break; + case EVQ3_WATER_LEAVE: // foot leaves + e = EV_WATER_LEAVE; + break; + case EVQ3_WATER_UNDER: // head touches + e = EV_WATER_UNDER; + break; + case EVQ3_WATER_CLEAR: // head leaves + e = EV_WATER_CLEAR; + break; + case EVQ3_ITEM_PICKUP: // normal item pickups are predictable + e = EV_ITEM_PICKUP; + break; + case EVQ3_GLOBAL_ITEM_PICKUP: // powerup / team sounds are broadcast to everyone + e = EV_GLOBAL_ITEM_PICKUP; + break; + case EVQ3_NOAMMO: + e = EV_NOAMMO; + break; + case EVQ3_CHANGE_WEAPON: + e = EV_CHANGE_WEAPON; + break; + case EVQ3_FIRE_WEAPON: + e = EV_FIRE_WEAPON; + break; + case EVQ3_USE_ITEM0: + e = EV_USE_ITEM0; + break; + case EVQ3_USE_ITEM1: + e = EV_USE_ITEM1; + break; + case EVQ3_USE_ITEM2: + e = EV_USE_ITEM2; + break; + case EVQ3_USE_ITEM3: + e = EV_USE_ITEM3; + break; + case EVQ3_USE_ITEM4: + e = EV_USE_ITEM4; + break; + case EVQ3_USE_ITEM5: + e = EV_USE_ITEM5; + break; + case EVQ3_USE_ITEM6: + e = EV_USE_ITEM6; + break; + case EVQ3_USE_ITEM7: + e = EV_USE_ITEM7; + break; + case EVQ3_USE_ITEM8: + e = EV_USE_ITEM8; + break; + case EVQ3_USE_ITEM9: + e = EV_USE_ITEM9; + break; + case EVQ3_USE_ITEM10: + e = EV_USE_ITEM10; + break; + case EVQ3_USE_ITEM11: + e = EV_USE_ITEM11; + break; + case EVQ3_USE_ITEM12: + e = EV_USE_ITEM12; + break; + case EVQ3_USE_ITEM13: + e = EV_USE_ITEM13; + break; + case EVQ3_USE_ITEM14: + e = EV_USE_ITEM14; + break; + case EVQ3_USE_ITEM15: + e = EV_USE_ITEM15; + break; + case EVQ3_ITEM_RESPAWN: + e = EV_ITEM_RESPAWN; + break; + case EVQ3_ITEM_POP: + e = EV_ITEM_POP; + break; + case EVQ3_PLAYER_TELEPORT_IN: + e = EV_PLAYER_TELEPORT_IN; + break; + case EVQ3_PLAYER_TELEPORT_OUT: + e = EV_PLAYER_TELEPORT_OUT; + break; + case EVQ3_GRENADE_BOUNCE: // eventParm will be the soundindex + e = EV_GRENADE_BOUNCE; + break; + case EVQ3_GENERAL_SOUND: + e = EV_GENERAL_SOUND; + break; + case EVQ3_GLOBAL_SOUND: // no attenuation + e = EV_GLOBAL_SOUND; + break; + case EVQ3_GLOBAL_TEAM_SOUND: + e = EV_GLOBAL_TEAM_SOUND; + break; + case EVQ3_BULLET_HIT_FLESH: + e = EV_BULLET_HIT_FLESH; + break; + case EVQ3_BULLET_HIT_WALL: + e = EV_BULLET_HIT_WALL; + break; + case EVQ3_MISSILE_HIT: + e = EV_MISSILE_HIT; + break; + case EVQ3_MISSILE_MISS: + e = EV_MISSILE_MISS; + break; + case EVQ3_MISSILE_MISS_METAL: + e = EV_MISSILE_MISS_METAL; + break; + case EVQ3_RAILTRAIL: + e = EV_RAILTRAIL; + break; + case EVQ3_SHOTGUN: + e = EV_SHOTGUN; + break; + case EVQ3_BULLET: // otherEntity is the shooter + Com_Printf("FIXME q3 EV_BULLET\n"); + //e = EV_BULLET; + e = EV_BULLET_HIT_FLESH; + break; + case EVQ3_PAIN: + e = EV_PAIN; + break; + case EVQ3_DEATH1: + e = EV_DEATH1; + break; + case EVQ3_DEATH2: + e = EV_DEATH2; + break; + case EVQ3_DEATH3: + e = EV_DEATH3; + break; + case EVQ3_OBITUARY: + e = EV_OBITUARY; + break; + case EVQ3_POWERUP_QUAD: + e = EV_POWERUP_QUAD; + break; + case EVQ3_POWERUP_BATTLESUIT: + e = EV_POWERUP_BATTLESUIT; + break; + case EVQ3_POWERUP_REGEN: + e = EV_POWERUP_REGEN; + break; + case EVQ3_GIB_PLAYER: // gib a previously living player + e = EV_GIB_PLAYER; + break; + case EVQ3_SCOREPLUM: // score plum + e = EV_SCOREPLUM; + break; + //#ifdef MISSIONPACK + case EVQ3_PROXIMITY_MINE_STICK: + e = EV_PROXIMITY_MINE_STICK ; + break; + case EVQ3_PROXIMITY_MINE_TRIGGER: + e = EV_PROXIMITY_MINE_TRIGGER ; + break; + case EVQ3_KAMIKAZE: // kamikaze explodes + e = EV_KAMIKAZE; + break; + case EVQ3_OBELISKEXPLODE: // obelisk explodes + e = EV_OBELISKEXPLODE; + break; + case EVQ3_OBELISKPAIN: // obelisk is in pain + e = EV_OBELISKPAIN; + break; + case EVQ3_INVUL_IMPACT: // invulnerability sphere impact + e = EV_INVUL_IMPACT; + break; + case EVQ3_JUICED: // invulnerability juiced effect + e = EV_JUICED; + break; + case EVQ3_LIGHTNINGBOLT: // lightning bolt bounced of invulnerability sphere + e = EV_LIGHTNINGBOLT; + break; + //#endif + case EVQ3_DEBUG_LINE: + e = EV_DEBUG_LINE; + break; + case EVQ3_STOPLOOPINGSOUND: + e = EV_STOPLOOPINGSOUND; + break; + case EVQ3_TAUNT: + e = EV_TAUNT; + break; + case EVQ3_TAUNT_YES: + e = EV_TAUNT_YES; + break; + case EVQ3_TAUNT_NO: + e = EV_TAUNT_NO; + break; + case EVQ3_TAUNT_FOLLOWME: + e = EV_TAUNT_FOLLOWME; + break; + case EVQ3_TAUNT_GETFLAG: + e = EV_TAUNT_GETFLAG; + break; + case EVQ3_TAUNT_GUARDBASE: + e = EV_TAUNT_GUARDBASE; + break; + case EVQ3_TAUNT_PATROL: + e = EV_TAUNT_PATROL; + break; + + default: + Com_Printf("unknown q3 event %d\n", event); + e = event; + break; + } } if (e == -1) { @@ -1476,7 +1747,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { es = ¢->currentState; event = es->event & ~EV_EVENT_BITS; - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { event = CG_EntityEventFromQ3(event); } @@ -1486,7 +1757,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { CG_Printf("id:%d ent:%3i event:%3i serverTime: %d ", id, es->number, event, cg.snap->serverTime); } - if (cent->currentState.eType >= ET_EVENTS) { + if ((cgs.realProtocol >= 46 && cent->currentState.eType >= ET_EVENTS) || (cgs.realProtocol < 46 && cent->currentState.eType >= (ET_EVENTS - 1))) { //Com_Printf("yes %d\n", cent->currentState.eType); if (cg.filter.events) { return; @@ -2034,7 +2305,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { DEBUGNAME("EV_FIRE_WEAPON"); if (es->number >= MAX_CLIENTS) { - if (cgs.protocol == PROTOCOL_QL && es->weapon == WP_GRENADE_LAUNCHER) { + if (cgs.protocolClass == PROTOCOL_QL && es->weapon == WP_GRENADE_LAUNCHER) { // this is ok, can have 'world' weapons like the grenades // in silentnight.bsp } else { @@ -2661,7 +2932,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { CG_StartSound (NULL, es->number, CHAN_VOICE, cgs.gameSounds[ n ] ); } else { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = CG_ConfigString( CS_SOUNDS + n - 1); } else { s = CG_ConfigString(CS_SOUNDS + n); @@ -2697,7 +2968,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { else CG_StartSound (NULL, wcg.clientNum, CHAN_AUTO, cgs.gameSounds[n]); } else { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = CG_ConfigString( CS_SOUNDS + n - 1); } else { s = CG_ConfigString(CS_SOUNDS + n); @@ -3316,7 +3587,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { //CG_PrintEntityStatep(es); - if (cgs.protocol == PROTOCOL_Q3 && CG_WaterLevel(&cg_entities[clientNum]) == 3) { + if (cgs.protocolClass == PROTOCOL_Q3 && CG_WaterLevel(&cg_entities[clientNum]) == 3) { CG_StartSound(NULL, clientNum, CHAN_VOICE, CG_CustomSound(clientNum, "*drown.wav")); } else { CG_StartSound( NULL, clientNum, CHAN_VOICE, @@ -3453,7 +3724,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { case EV_GIB_PLAYER: DEBUGNAME("EV_GIB_PLAYER"); if (es->number >= MAX_CLIENTS) { - if (1) { //(!cgs.protocol == PROTOCOL_QL) { + if (1) { //(!cgs.protocolClass == PROTOCOL_QL) { // can't get client number in ql //CG_Printf("^3FIXME event %d %s num %d clientNum %d\n", event, eventName, es->number, es->clientNum); //CG_PrintEntityStatep(es); @@ -3546,7 +3817,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { case EV_THAW_PLAYER: { DEBUGNAME("EV_THAW_PLAYER"); if (es->number >= MAX_CLIENTS) { - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { // can't get client number in ql CG_Printf("^3FIXME event %d %s num %d clientNum %d\n", event, eventName, es->number, es->clientNum); //Com_Printf("158 %s\n", cgs.clientinfo[es->number - 158].name); @@ -3564,7 +3835,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { case EV_THAW_TICK: { DEBUGNAME("EV_THAW_TICK"); if (es->number >= MAX_CLIENTS) { - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { // can't get client number in ql CG_Printf("^3FIXME event %d %s num %d clientNum %d\n", event, eventName, es->number, es->clientNum); } @@ -3699,6 +3970,7 @@ void CG_EntityEvent( centity_t *cent, const vec3_t position ) { DEBUGNAME("EV_AWARD"); + //Com_Printf("event EV_AWARD %d %d %d\n", es->clientNum, es->modelindex2, es->generic1); // clientNum: clientNum // modelindex2: count // generic1: award type @@ -3852,7 +4124,7 @@ CG_CheckEvents */ void CG_CheckEvents( centity_t *cent ) { // check for event-only entities - if ( cent->currentState.eType > ET_EVENTS ) { + if ( (cgs.realProtocol >= 46 && cent->currentState.eType > ET_EVENTS) || (cgs.realProtocol < 46 && cent->currentState.eType > (ET_EVENTS - 1)) ) { if ( cent->previousEvent ) { //Com_Printf("%d %d already fired\n", cent - cg_entities, cent->currentState.eType - ET_EVENTS); return; // already fired @@ -3864,7 +4136,11 @@ void CG_CheckEvents( centity_t *cent ) { cent->previousEvent = 1; - cent->currentState.event = cent->currentState.eType - ET_EVENTS; + if (cgs.realProtocol < 46) { + cent->currentState.event = cent->currentState.eType - ET_EVENTS + 1; + } else { + cent->currentState.event = cent->currentState.eType - ET_EVENTS; + } } else { // check for events riding with another entity if ( cent->currentState.event == cent->previousEvent ) { diff --git a/code/cgame/cg_info.c b/code/cgame/cg_info.c index c760bf4..a02fd9a 100644 --- a/code/cgame/cg_info.c +++ b/code/cgame/cg_info.c @@ -371,10 +371,10 @@ void CG_DrawInformation (qboolean loading) y += PROP_HEIGHT * lines; // ranked or mod - if (cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { value = atoi(Info_ValueForKey(info, "sv_ranked")); Com_sprintf(buf, sizeof(buf), "(%s server)", value ? "ranked" : "unranked"); - } else if (cgs.protocol == PROTOCOL_Q3) { // mod: baseq3, osp, cpma, etc.. + } else if (cgs.protocolClass == PROTOCOL_Q3) { // mod: baseq3, osp, cpma, etc.. if (cgs.ospEncrypt) { Com_sprintf(buf, sizeof(buf), "osp ^6encrypted"); } else if (cgs.osp) { @@ -488,7 +488,7 @@ void CG_DrawInformation (qboolean loading) s = "Unknown Gametype"; } - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { lines = UI_DrawProportionalString3(320, y, s, UI_CENTER | UI_SMALLFONT | UI_DROPSHADOW, colorYellow); y += PROP_HEIGHT * lines; } else { @@ -556,7 +556,7 @@ void CG_DrawInformation (qboolean loading) } } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { const char *p; if (cgs.realProtocol < 91) { diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index 3624a18..1742932 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -2503,7 +2503,7 @@ typedef struct { qboolean serverHaveCustomModelString; qboolean serverAllowCustomHead; int realProtocol; - int protocol; + int protocolClass; qboolean cpma; int cpmaVersionMajor; int cpmaVersionMinor; diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index 516aeb9..5e4d770 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -3634,7 +3634,7 @@ static void CG_RegisterSounds( void ) { cg.proxTickSoundIndex = -999; for ( i = 1 ; i < MAX_SOUNDS ; i++ ) { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { soundName = CG_ConfigString(CS_SOUNDS +i - 1); } else { soundName = CG_ConfigString(CS_SOUNDS + i); @@ -4548,7 +4548,7 @@ void CG_BuildSpectatorString(void) { sc = &cg.scores[i]; if (sc->team == TEAM_SPECTATOR) { - if (cg_spectatorListSkillRating.integer && cgs.clientinfo[sc->client].knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (cg_spectatorListSkillRating.integer && cgs.clientinfo[sc->client].knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { Q_strcat(slist, sizeof(slist), va("^5(%d)", cgs.clientinfo[sc->client].skillRating)); } if (*cgs.clientinfo[sc->client].clanTag) { @@ -4561,7 +4561,7 @@ void CG_BuildSpectatorString(void) { if (cg_spectatorListScore.integer) { Q_strcat(slist, sizeof(slist), va("^3(%d)", sc->score)); } - if (cg_spectatorListQue.integer && cgs.protocol == PROTOCOL_QL && CG_IsDuelGame(cgs.gametype)) { + if (cg_spectatorListQue.integer && cgs.protocolClass == PROTOCOL_QL && CG_IsDuelGame(cgs.gametype)) { if (cgs.clientinfo[sc->client].spectatorOnly) { Q_strcat(slist, sizeof(slist), "^7(^5s^7)"); } else { @@ -4573,7 +4573,7 @@ void CG_BuildSpectatorString(void) { #if 0 //Com_Printf("%s\n", cgs.clientinfo[sc->client].name); - if (cgs.clientinfo[sc->client].knowSkillRating && cg_spectatorListSkillRating.integer && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (cgs.clientinfo[sc->client].knowSkillRating && cg_spectatorListSkillRating.integer && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { //Com_Printf("know for %s\n", cgs.clientinfo[sc->client].name); if (*cgs.clientinfo[sc->client].clanTag) { Q_strcat(slist, sizeof(slist), va("^5(%d)%s %s^3(%d)^7 ", cgs.clientinfo[sc->client].skillRating, cgs.clientinfo[sc->client].clanTag, cgs.clientinfo[sc->client].name, sc->score)); @@ -4727,7 +4727,11 @@ qboolean CG_ConfigStringIndexToQ3 (int *index) } if (*index >= CS_LOCATIONS && *index < CS_LOCATIONS + MAX_LOCATIONS) { n = *index - CS_LOCATIONS; - *index = CSQ3_LOCATIONS + n; + if (cgs.realProtocol < 46) { + *index = CSQ3DM3_LOCATIONS + n; + } else { + *index = CSQ3_LOCATIONS + n; + } return qtrue; } @@ -4808,10 +4812,18 @@ qboolean CG_ConfigStringIndexToQ3 (int *index) n = CSQ3_SOUNDS; break; case CS_LOCATIONS: - n = CSQ3_LOCATIONS; + if (cgs.realProtocol < 46) { + n = CSQ3DM3_LOCATIONS; + } else { + n = CSQ3_LOCATIONS; + } break; case CS_PARTICLES: - n = CSQ3_PARTICLES; + if (cgs.realProtocol < 46) { + n = CSQ3DM3_PARTICLES; + } else { + n = CSQ3_PARTICLES; + } break; default: @@ -4859,96 +4871,90 @@ qboolean CG_ConfigStringIndexFromQ3 (int *index) *index = CS_PLAYERS + n; return qtrue; } - if (*index >= CSQ3_LOCATIONS && *index < CSQ3_LOCATIONS + MAX_LOCATIONS) { - n = *index - CSQ3_LOCATIONS; - *index = CS_LOCATIONS + n; - return qtrue; + + if (cgs.realProtocol < 46) { + if (*index >= CSQ3DM3_LOCATIONS && *index < CSQ3DM3_LOCATIONS + MAX_LOCATIONS) { + n = *index - CSQ3DM3_LOCATIONS; + *index = CS_LOCATIONS + n; + return qtrue; + } else if (*index >= CSQ3DM3_PARTICLES && *index < CSQ3DM3_PARTICLES + MAX_LOCATIONS) { + n = *index - CSQ3DM3_PARTICLES; + *index = CS_PARTICLES + n; + return qtrue; + } + } else { + if (*index >= CSQ3_LOCATIONS && *index < CSQ3_LOCATIONS + MAX_LOCATIONS) { + n = *index - CSQ3_LOCATIONS; + *index = CS_LOCATIONS + n; + return qtrue; + } else if (*index >= CSQ3_PARTICLES && *index < CSQ3_PARTICLES + MAX_LOCATIONS) { + n = *index - CSQ3_PARTICLES; + *index = CS_PARTICLES + n; + return qtrue; + } } - switch (*index) { - case CSQ3_SERVERINFO: + if (*index == CSQ3_SERVERINFO) { n = CS_SERVERINFO; - break; - case CSQ3_SYSTEMINFO: + } else if (*index == CSQ3_SYSTEMINFO) { n = CS_SYSTEMINFO; - break; - case CSQ3_MUSIC: + } else if (*index == CSQ3_MUSIC) { n = CS_MUSIC; - break; - case CSQ3_MESSAGE: + } else if (*index == CSQ3_MESSAGE) { n = CS_MESSAGE; - break; - case CSQ3_MOTD: + } else if (*index == CSQ3_MOTD) { n = CS_MOTD; - break; - case CSQ3_WARMUP: + } else if (*index == CSQ3_WARMUP) { n = CS_WARMUP; - break; - case CSQ3_SCORES1: + } else if (*index == CSQ3_SCORES1) { n = CS_SCORES1; - break; - case CSQ3_SCORES2: + } else if (*index == CSQ3_SCORES2) { n = CS_SCORES2; - break; - case CSQ3_VOTE_TIME: + } else if (*index == CSQ3_VOTE_TIME) { n = CS_VOTE_TIME; - break; - case CSQ3_VOTE_STRING: + } else if (*index == CSQ3_VOTE_STRING) { n = CS_VOTE_STRING; - break; - case CSQ3_VOTE_YES: + } else if (*index == CSQ3_VOTE_YES) { n = CS_VOTE_YES; - break; - case CSQ3_VOTE_NO: + } else if (*index == CSQ3_VOTE_NO) { n = CS_VOTE_NO; - break; - case CSQ3_TEAMVOTE_TIME: + } else if (*index == CSQ3_TEAMVOTE_TIME) { n = CS_TEAMVOTE_TIME; - break; - case CSQ3_TEAMVOTE_STRING: + } else if (*index == CSQ3_TEAMVOTE_STRING) { n = CS_TEAMVOTE_STRING; - break; - case CSQ3_TEAMVOTE_YES: + } else if (*index == CSQ3_TEAMVOTE_YES) { n = CS_TEAMVOTE_YES; - break; - case CSQ3_TEAMVOTE_NO: + } else if (*index == CSQ3_TEAMVOTE_NO) { n = CS_TEAMVOTE_NO; - break; - case CSQ3_GAME_VERSION: + } else if (*index == CSQ3_GAME_VERSION) { n = CS_GAME_VERSION; - break; - case CSQ3_LEVEL_START_TIME: + } else if (*index == CSQ3_LEVEL_START_TIME) { n = CS_LEVEL_START_TIME; - break; - case CSQ3_INTERMISSION: + } else if (*index == CSQ3_INTERMISSION) { n = CS_INTERMISSION; - break; - case CSQ3_FLAGSTATUS: + } else if (*index == CSQ3_FLAGSTATUS) { n = CS_FLAGSTATUS; - break; - case CSQ3_SHADERSTATE: + } else if (*index == CSQ3_SHADERSTATE) { n = CS_SHADERSTATE; - break; - case CSQ3_BOTINFO: + } else if (*index == CSQ3_BOTINFO) { n = CS_BOTINFO; - break; - case CSQ3_ITEMS: + } else if (*index == CSQ3_ITEMS) { n = CS_ITEMS; - break; - case CSQ3_MODELS: + } else if (*index == CSQ3_MODELS) { n = CS_MODELS; - break; - case CSQ3_SOUNDS: + } else if (*index == CSQ3_SOUNDS) { n = CS_SOUNDS; - break; - case CSQ3_LOCATIONS: +#if 0 // these are handled above, use ranges + } else if (*index == CSQ3_LOCATIONS && cgs.realProtocol >= 46) { n = CS_LOCATIONS; - break; - case CSQ3_PARTICLES: + } else if (*index == CSQ3DM3_LOCATIONS && cgs.realProtocol < 46) { + n = CS_LOCATIONS; + } else if (*index == CSQ3_PARTICLES && cgs.realProtocol >= 46) { n = CS_PARTICLES; - break; - - default: + } else if (*index == CSQ3DM3_PARTICLES && cgs.realProtocol < 46) { + n = CS_PARTICLES; +#endif + } else { //Com_Printf("unknown q3 config string %d\n", *index); n = *index; return qfalse; @@ -5064,7 +5070,7 @@ const char *CG_ConfigString( int index ) { } n = index; - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { CG_ConfigStringIndexToQ3(&n); } @@ -6102,14 +6108,14 @@ static const char *CG_FeederItemTextCa (float feederID, int index, int column, q *handle = cgs.clientinfoOrig[sp->client].modelIcon; return ""; case 1: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.clientinfoOrig[sp->client].premiumSubscriber) { *handle = cgs.media.premiumIcon; } } return ""; case 2: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -6146,7 +6152,7 @@ static const char *CG_FeederItemTextCa (float feederID, int index, int column, q } // using obituary to track players still alive - if (cgs.protocol == PROTOCOL_QL || cgs.cpma) { + if (cgs.protocolClass == PROTOCOL_QL || cgs.cpma) { alive = wclients[sp->client].aliveThisRound; } @@ -6167,7 +6173,7 @@ static const char *CG_FeederItemTextCa (float feederID, int index, int column, q } break; case 5: - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol == PROTOCOL_Q3) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass == PROTOCOL_Q3) { clanTag = info->clanTag; if (*clanTag) { s = va("^7%s ^7%s", clanTag, info->name); @@ -6178,7 +6184,7 @@ static const char *CG_FeederItemTextCa (float feederID, int index, int column, q return s; } else { clanTag = info->clanTag; - if (info->knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (info->knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { if (*clanTag) { s = va("^1%d ^7%s ^7%s", info->skillRating, clanTag, info->name); } else { @@ -6303,7 +6309,7 @@ static const char *CG_FeederItemTextTdm (float feederID, int index, int column, if (info && info->infoValid) { switch (column) { case 0: - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol != PROTOCOL_QL) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass != PROTOCOL_QL) { *handle = cgs.clientinfoOrig[sp->client].modelIcon; } else { // default is cg_scoreBoardStyle.integer == 1 int bestWeapon; @@ -6321,14 +6327,14 @@ static const char *CG_FeederItemTextTdm (float feederID, int index, int column, } return ""; case 1: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.clientinfoOrig[sp->client].premiumSubscriber) { *handle = cgs.media.premiumIcon; } } return ""; case 2: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -6364,7 +6370,7 @@ static const char *CG_FeederItemTextTdm (float feederID, int index, int column, } break; case 5: - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol == PROTOCOL_Q3) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass == PROTOCOL_Q3) { clanTag = info->clanTag; if (*clanTag) { s = va("^7%s ^7%s", clanTag, info->name); @@ -6386,7 +6392,7 @@ static const char *CG_FeederItemTextTdm (float feederID, int index, int column, } clanTag = info->clanTag; - if (info->knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (info->knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { if (*clanTag) { s = va("^3%3d ^1%d ^7%s ^7%s", accuracy, info->skillRating, clanTag, info->name); } else { @@ -6510,7 +6516,7 @@ static const char *CG_FeederItemTextCtf (float feederID, int index, int column, if (info && info->infoValid) { switch (column) { case 0: - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol != PROTOCOL_QL) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass != PROTOCOL_QL) { if (cgs.cpma && cgs.gametype == GT_NTF && cg_cpmaNtfScoreboardClassModel.integer) { //FIXME 2021-09-06 red and blue icons *handle = cg.ntfClassModel[cgs.clientinfo[sp->client].ntfClass].modelIcon; @@ -6529,14 +6535,14 @@ static const char *CG_FeederItemTextCtf (float feederID, int index, int column, } return ""; case 1: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.clientinfoOrig[sp->client].premiumSubscriber) { *handle = cgs.media.premiumIcon; } } return ""; case 2: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -6603,7 +6609,7 @@ static const char *CG_FeederItemTextCtf (float feederID, int index, int column, } break; case 5: - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol == PROTOCOL_Q3) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass == PROTOCOL_Q3) { clanTag = info->clanTag; if (*clanTag) { s = va("^7%s ^7%s", clanTag, info->name); @@ -6623,7 +6629,7 @@ static const char *CG_FeederItemTextCtf (float feederID, int index, int column, } clanTag = info->clanTag; - if (info->knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (info->knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { if (*clanTag) { s = va("^3%3d ^1%d ^7%s ^7%s", accuracy, info->skillRating, clanTag, info->name); } else { @@ -6728,7 +6734,7 @@ static const char *CG_FeederItemTextFreezetag (float feederID, int index, int co if (info && info->infoValid) { switch (column) { case 0: - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol != PROTOCOL_QL) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass != PROTOCOL_QL) { *handle = cgs.clientinfoOrig[sp->client].modelIcon; } else { // default is cg_scoreBoardStyle.integer == 1 int bestWeapon; @@ -6747,7 +6753,7 @@ static const char *CG_FeederItemTextFreezetag (float feederID, int index, int co return ""; case 1: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.clientinfoOrig[sp->client].premiumSubscriber) { *handle = cgs.media.premiumIcon; } @@ -6755,7 +6761,7 @@ static const char *CG_FeederItemTextFreezetag (float feederID, int index, int co return ""; case 2: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -6800,7 +6806,7 @@ static const char *CG_FeederItemTextFreezetag (float feederID, int index, int co } } #endif - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { alive = wclients[sp->client].aliveThisRound; } @@ -6843,7 +6849,7 @@ static const char *CG_FeederItemTextFreezetag (float feederID, int index, int co } clanTag = info->clanTag; - if (info->knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (info->knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { if (*clanTag) { s = va("^3%3d ^1%d ^7%s ^7%s", accuracy, info->skillRating, clanTag, info->name); } else { @@ -6968,7 +6974,7 @@ static const char *CG_FeederItemTextFfa (float feederID, int index, int column, switch (column) { case 0: #if 0 - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol != PROTOCOL_QL) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass != PROTOCOL_QL) { *handle = cgs.clientinfoOrig[sp->client].modelIcon; } else if (cgs.clientinfoOrig[sp->client].countryFlag && cg_scoreBoardStyle.integer == 2) { *handle = cgs.clientinfoOrig[sp->client].countryFlag; @@ -6983,7 +6989,7 @@ static const char *CG_FeederItemTextFfa (float feederID, int index, int column, *handle = cgs.clientinfoOrig[sp->client].modelIcon; return ""; case 1: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.clientinfoOrig[sp->client].premiumSubscriber) { *handle = cgs.media.premiumIcon; } @@ -6991,7 +6997,7 @@ static const char *CG_FeederItemTextFfa (float feederID, int index, int column, return ""; case 2: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -7041,7 +7047,7 @@ static const char *CG_FeederItemTextFfa (float feederID, int index, int column, return s; } else { clanTag = info->clanTag; - if (info->knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (info->knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { if (*clanTag) { s = va("^3%3d ^1%d ^7%s ^7%s", sp->accuracy, info->skillRating, clanTag, info->name); } else { @@ -7153,7 +7159,7 @@ static const char *CG_FeederItemTextRedRover (float feederID, int index, int col case 0: #if 0 - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol != PROTOCOL_QL) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass != PROTOCOL_QL) { *handle = cgs.clientinfoOrig[sp->client].modelIcon; } else if (cgs.clientinfoOrig[sp->client].countryFlag && cg_scoreBoardStyle.integer == 2) { *handle = cgs.clientinfoOrig[sp->client].countryFlag; @@ -7168,14 +7174,14 @@ static const char *CG_FeederItemTextRedRover (float feederID, int index, int col *handle = cgs.clientinfoOrig[sp->client].modelIcon; return ""; case 1: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.clientinfoOrig[sp->client].premiumSubscriber) { *handle = cgs.media.premiumIcon; } } return ""; case 2: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -7223,7 +7229,7 @@ static const char *CG_FeederItemTextRedRover (float feederID, int index, int col return s; } else { clanTag = info->clanTag; - if (info->knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (info->knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { if (*clanTag) { s = va("^3%3d ^1%d ^7%s ^7%s", sp->accuracy, info->skillRating, clanTag, info->name); } else { @@ -7328,7 +7334,7 @@ static const char *CG_FeederItemTextRace (float feederID, int index, int column, if (info && info->infoValid) { switch (column) { case 0: - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol != PROTOCOL_QL) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass != PROTOCOL_QL) { *handle = cgs.clientinfoOrig[sp->client].modelIcon; } else if (cgs.clientinfoOrig[sp->client].countryFlag && cg_scoreBoardStyle.integer == 2) { *handle = cgs.clientinfoOrig[sp->client].countryFlag; @@ -7340,7 +7346,7 @@ static const char *CG_FeederItemTextRace (float feederID, int index, int column, } return ""; case 1: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.clientinfoOrig[sp->client].premiumSubscriber) { *handle = cgs.media.premiumIcon; } @@ -7351,7 +7357,7 @@ static const char *CG_FeederItemTextRace (float feederID, int index, int column, return va("^3%d%%", info->handicap); } #endif - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -7399,7 +7405,7 @@ static const char *CG_FeederItemTextRace (float feederID, int index, int column, return s; } else { clanTag = info->clanTag; - if (info->knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (info->knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { if (*clanTag) { s = va("^3%3d ^1%d ^7%s ^7%s", sp->accuracy, info->skillRating, clanTag, info->name); } else { @@ -7511,7 +7517,7 @@ static const char *CG_FeederItemText (float feederID, int index, int column, qha if (info && info->infoValid) { switch (column) { case 0: - if (cg_scoreBoardStyle.integer == 0 || cgs.protocol != PROTOCOL_QL) { + if (cg_scoreBoardStyle.integer == 0 || cgs.protocolClass != PROTOCOL_QL) { *handle = cgs.clientinfoOrig[sp->client].modelIcon; } else if (cgs.clientinfoOrig[sp->client].countryFlag && cg_scoreBoardStyle.integer == 2) { *handle = cgs.clientinfoOrig[sp->client].countryFlag; @@ -7523,12 +7529,12 @@ static const char *CG_FeederItemText (float feederID, int index, int column, qha } return ""; case 1: - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.clientinfoOrig[sp->client].premiumSubscriber) { *handle = cgs.media.premiumIcon; } } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -7562,7 +7568,7 @@ static const char *CG_FeederItemText (float feederID, int index, int column, qha alive = sp->alive; // using obituary to track players still alive - if (cgs.protocol == PROTOCOL_QL || cgs.cpma) { + if (cgs.protocolClass == PROTOCOL_QL || cgs.cpma) { alive = wclients[sp->client].aliveThisRound; } @@ -7577,7 +7583,7 @@ static const char *CG_FeederItemText (float feederID, int index, int column, qha } if (cg_scoreBoardStyle.integer == 0) { //FIXME spacing - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int startingHealth; startingHealth = atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_startingHealth")); @@ -7606,7 +7612,7 @@ static const char *CG_FeederItemText (float feederID, int index, int column, qha return s; } else { clanTag = info->clanTag; - if (info->knowSkillRating && cgs.protocol == PROTOCOL_QL && cgs.realProtocol < 91) { + if (info->knowSkillRating && cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol < 91) { if (*clanTag) { s = va("^3%3d ^1%d ^7%s ^7%s", sp->accuracy, info->skillRating, clanTag, info->name); } else { @@ -7933,12 +7939,12 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client cg.demoPlayback = demoPlayback; cgs.realProtocol = SC_Cvar_Get_Int("protocol"); Com_Printf("client set protocol: %d\n", cgs.realProtocol); - if (cgs.realProtocol >= 66 && cgs.realProtocol <= 71) { - cgs.protocol = PROTOCOL_Q3; + if (cgs.realProtocol >= 43 && cgs.realProtocol <= 71) { + cgs.protocolClass = PROTOCOL_Q3; } else if (cgs.realProtocol == 73 || cgs.realProtocol == 90 || cgs.realProtocol == 91) { - cgs.protocol = PROTOCOL_QL; + cgs.protocolClass = PROTOCOL_QL; } else { - cgs.protocol = PROTOCOL_QL; + cgs.protocolClass = PROTOCOL_QL; } //Com_Printf("^3ctfs %d\n", GT_CTFS); @@ -7964,7 +7970,7 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client PW_NUM_POWERUPS = PW91_NUM_POWERUPS; } - if (cgs.protocol == PROTOCOL_Q3) { //FIXME hack + if (cgs.protocolClass == PROTOCOL_Q3) { //FIXME hack // start as baseq3 memcpy(&bg_itemlist, &bg_itemlistQ3, sizeof(gitem_t) * bg_numItemsQ3); bg_numItems = bg_numItemsQ3; @@ -8007,6 +8013,12 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client PERS_ASSIST_COUNT = 12; PERS_GAUNTLET_FRAG_COUNT = 13; PERS_CAPTURES = 14; + + if (cgs.realProtocol < 46) { + //FIXME double check 46 might just be missing PERS_CAPTURES + // no PERS_DEFEND_COUNT, PERS_ASSIST_COUNT, or PERS_CAPTURES + PERS_GAUNTLET_FRAG_COUNT = 11; + } } // fx scripting uses cent->currentState.clientNum and it can happen @@ -8113,7 +8125,7 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client trap_GetGameState( &cgs.gameState ); cgs.rocketSpeed = 900; - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { cgs.rocketSpeed = 800; if (!Q_stricmp("cpma-1", CG_ConfigString(CS_GAME_VERSION))) { //FIXME hack cgs.cpma = qtrue; @@ -8147,6 +8159,18 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client Com_Printf("^5q3plus detected\n"); cgs.q3plus = qtrue; } + + if (cgs.realProtocol == 46) { + // team arena items might have been accidentally enabled + //FIXME others? not in Q3 1.25v + if (!Q_stricmpn("Q3 1.25p", Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "version"), strlen("Q3 1.25p"))) { + //Com_Printf("^5sldfkjsldkfjlsdkfjdkfj\n"); + memcpy(&bg_itemlist, &bg_itemlistQ3_125p, sizeof(gitem_t) * bg_numItemsQ3_125p); + bg_numItems = bg_numItemsQ3_125p; + WP_NUM_WEAPONS = 14; // no hmg + } + } + } else if (cgs.realProtocol == 73) { memcpy(&bg_itemlist, &bg_itemlistQldm73, sizeof(gitem_t) * bg_numItemsQldm73); bg_numItems = bg_numItemsQldm73; @@ -8185,7 +8209,7 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client cgs.levelStartTime = atoi( s ); CG_ParseServerinfo(qtrue, qfalse); - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { Com_Printf("^5ql%s ^5version %d.%d.%d.%d\n", cgs.isQuakeLiveBetaDemo ? " ^6beta" : "", cgs.qlversion[0], cgs.qlversion[1], cgs.qlversion[2], cgs.qlversion[3]); } @@ -8267,7 +8291,7 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client // quakelive custom game modes, /devmap /give item.. just have to // always load all weapons so that projectiles will be present //FIXME ugh, terrible hack - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int idx; //for (item = bg_itemlist + 1; item->classname; item++) { @@ -8411,7 +8435,7 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client CG_ReloadQ3mmeScripts(cg_fxfile.string); CG_CreateNameSprites(); - if (cgs.protocol == PROTOCOL_QL && cgs.customServerSettings & SERVER_SETTING_INFECTED && cgs.gametype == GT_RED_ROVER) { + if (cgs.protocolClass == PROTOCOL_QL && cgs.customServerSettings & SERVER_SETTING_INFECTED && cgs.gametype == GT_RED_ROVER) { CG_LoadInfectedGameTypeModels(); } @@ -8483,7 +8507,7 @@ static void CG_Init (int serverMessageNum, int serverCommandSequence, int client if (cg.demoPlayback) { trap_Get_Demo_Timeouts(&cgs.numTimeouts, cgs.timeOuts); - if (cgs.protocol == PROTOCOL_QL || cgs.cpma) { + if (cgs.protocolClass == PROTOCOL_QL || cgs.cpma) { trap_GetRoundStartTimes(&cg.numRoundStarts, cg.roundStarts); } } diff --git a/code/cgame/cg_newdraw.c b/code/cgame/cg_newdraw.c index 4d87a33..f8fdea4 100644 --- a/code/cgame/cg_newdraw.c +++ b/code/cgame/cg_newdraw.c @@ -1222,7 +1222,7 @@ static void CG_Draw1stPlacePlayerModel (float x, float y, float w, float h) head.hModel = ci->headModel; head.customSkin = ci->headSkin; - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { weaponNum = WP_NONE; if (cgs.gametype == GT_TOURNAMENT) { @@ -1301,7 +1301,7 @@ static void CG_Draw1stPlacePlayerModel (float x, float y, float w, float h) head.hModel = ci->headModel; head.customSkin = ci->headSkin; - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { weaponNum = WP_NONE; if (cgs.gametype == GT_TOURNAMENT) { @@ -1709,7 +1709,7 @@ static void CG_OneFlagStatus (const rectDef_t *rect, qboolean colorize) static void CG_DrawCTFPowerUp(const rectDef_t *rect) { int value; - if (cgs.protocol == PROTOCOL_Q3) { //FIXME team arena + if (cgs.protocolClass == PROTOCOL_Q3) { //FIXME team arena return; } @@ -1721,7 +1721,7 @@ static void CG_DrawCTFPowerUp(const rectDef_t *rect) { value = cg.snap->ps.stats[STAT_PERSISTANT_POWERUP]; if ( value ) { //Com_Printf("ctf powerup %d\n", value); - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { //FIXME this is absolutely fucked up, why is this happening????? if (value == 0) { @@ -1927,7 +1927,7 @@ static void CG_DrawAreaPowerUp(const rectDef_t *rect, int align, float special, // kill counters if (cg_powerupKillCounter.integer && !wolfcam_following) { - if (item->giTag == PW_QUAD && cgs.protocol == PROTOCOL_QL && cg_quadKillCounter.integer == 1 && ps->stats[STAT_QUAD_KILL_COUNT] > 0) { + if (item->giTag == PW_QUAD && cgs.protocolClass == PROTOCOL_QL && cg_quadKillCounter.integer == 1 && ps->stats[STAT_QUAD_KILL_COUNT] > 0) { float sc; sc = 0.25; @@ -1958,7 +1958,7 @@ static void CG_DrawAreaPowerUp(const rectDef_t *rect, int align, float special, scale * 0.29333, color, va(" x %d", ps->stats[STAT_QUAD_KILL_COUNT]), 0, 0, 0, font); } } - if (item->giTag == PW_BATTLESUIT && cgs.protocol == PROTOCOL_QL && cg_battleSuitKillCounter.integer == 1 && ps->stats[STAT_BATTLE_SUIT_KILL_COUNT] > 0) { + if (item->giTag == PW_BATTLESUIT && cgs.protocolClass == PROTOCOL_QL && cg_battleSuitKillCounter.integer == 1 && ps->stats[STAT_BATTLE_SUIT_KILL_COUNT] > 0) { float sc; sc = 0.25; @@ -2393,7 +2393,7 @@ float CG_GetValue(int ownerDraw) { case CG_RED_TIMEOUT_COUNT: f = 0; - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { f = atof(CG_ConfigString(CS_RED_TEAM_TIMEOUTS_LEFT)); } return f; @@ -2401,7 +2401,7 @@ float CG_GetValue(int ownerDraw) { case CG_BLUE_TIMEOUT_COUNT: f = 0; - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { f = atof(CG_ConfigString(CS_BLUE_TEAM_TIMEOUTS_LEFT)); } return f; @@ -3691,7 +3691,7 @@ static qboolean CG_OwnerDrawVisible2 (int flags) if (flags & CG_SHOW_IF_PLYR1) { // this is only used in duel scoreboard to prevent seeing pickups from other player - if (cgs.protocol != PROTOCOL_QL || wolfcam_following || cg.demoPlayback) { + if (cgs.protocolClass != PROTOCOL_QL || wolfcam_following || cg.demoPlayback) { return qtrue; } @@ -3710,7 +3710,7 @@ static qboolean CG_OwnerDrawVisible2 (int flags) if (flags & CG_SHOW_IF_PLYR2) { // this is only used in duel scoreboard to prevent seeing pickups from other player - if (cgs.protocol != PROTOCOL_QL || wolfcam_following || cg.demoPlayback) { + if (cgs.protocolClass != PROTOCOL_QL || wolfcam_following || cg.demoPlayback) { return qtrue; } @@ -5376,7 +5376,7 @@ static void CG_Draw1stPlaceScore (const rectDef_t *rect, float scale, const vec4 int scoreStringLength; vec4_t scoreColor; - if (cgs.protocol == PROTOCOL_Q3 && !cgs.cpma) { + if (cgs.protocolClass == PROTOCOL_Q3 && !cgs.cpma) { CG_OspCalcPlacements(); } @@ -5600,7 +5600,7 @@ static void CG_Draw2ndPlaceScore (const rectDef_t *rect, float scale, const vec4 int scoreStringLength; vec4_t scoreColor; - if (cgs.protocol == PROTOCOL_Q3 && !cgs.cpma) { + if (cgs.protocolClass == PROTOCOL_Q3 && !cgs.cpma) { CG_OspCalcPlacements(); } @@ -6202,7 +6202,7 @@ void CG_DrawWeaponBar( void ) { } maxWeapons = WP_NUM_WEAPONS; - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { maxWeapons = WP_GRAPPLING_HOOK; } @@ -6556,7 +6556,7 @@ int CG_GetCurrentTimeWithDirection (int *numberOfOvertimes) // check overtime - if (cgs.protocol == PROTOCOL_Q3 && !cgs.cpma) { + if (cgs.protocolClass == PROTOCOL_Q3 && !cgs.cpma) { //FIXME } else if (cgs.realTimelimit) { if (timePlayed > (cgs.timelimit * 60 * 1000)) { @@ -7531,7 +7531,7 @@ static void CG_Draw1stPlayerPickups (const rectDef_t *rect, float scale, int sty textRect.y = y + h; CG_Text_Paint_Align(&textRect, scale, color, s, 0, 0, style, font, align); } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = va("%.2f", ds->redArmorTime); if (cg_wideScreen.integer == 7) { // 2018-10-04 fixed offsets @@ -7559,7 +7559,7 @@ static void CG_Draw1stPlayerPickups (const rectDef_t *rect, float scale, int sty textRect.y = y + h; CG_Text_Paint_Align(&textRect, scale, color, s, 0, 0, style, font, align); } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = va("%.2f", ds->yellowArmorTime); if (cg_wideScreen.integer == 7) { // 2018-10-04 fixed offsets @@ -7587,7 +7587,7 @@ static void CG_Draw1stPlayerPickups (const rectDef_t *rect, float scale, int sty textRect.y = y + h; CG_Text_Paint_Align(&textRect, scale, color, s, 0, 0, style, font, align); } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = va("%.2f", ds->greenArmorTime); if (cg_wideScreen.integer == 7) { // 2018-10-04 fixed offsets @@ -7615,7 +7615,7 @@ static void CG_Draw1stPlayerPickups (const rectDef_t *rect, float scale, int sty textRect.y = y + h; CG_Text_Paint_Align(&textRect, scale, color, s, 0, 0, style, font, align); } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = va("%.2f", ds->megaHealthTime); if (cg_wideScreen.integer == 7) { // 2018-10-04 fixed offsets @@ -7689,7 +7689,7 @@ static void CG_Draw2ndPlayerPickups (const rectDef_t *rect, float scale, int sty textRect.y = y + h; CG_Text_Paint_Align(&textRect, scale, color, s, 0, 0, style, font, align); } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = va("%.2f", ds->redArmorTime); if (cg_wideScreen.integer == 7) { // 2018-10-05 fixed offsets @@ -7717,7 +7717,7 @@ static void CG_Draw2ndPlayerPickups (const rectDef_t *rect, float scale, int sty textRect.y = y + h; CG_Text_Paint_Align(&textRect, scale, color, s, 0, 0, style, font, align); } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = va("%.2f", ds->yellowArmorTime); if (cg_wideScreen.integer == 7) { // 2018-10-05 fixed offsets @@ -7745,7 +7745,7 @@ static void CG_Draw2ndPlayerPickups (const rectDef_t *rect, float scale, int sty textRect.y = y + h; CG_Text_Paint_Align(&textRect, scale, color, s, 0, 0, style, font, align); } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = va("%.2f", ds->greenArmorTime); if (cg_wideScreen.integer == 7) { // 2018-10-05 fixed offsets @@ -7773,7 +7773,7 @@ static void CG_Draw2ndPlayerPickups (const rectDef_t *rect, float scale, int sty textRect.y = y + h; CG_Text_Paint_Align(&textRect, scale, color, s, 0, 0, style, font, align); } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { s = va("%.2f", ds->megaHealthTime); if (cg_wideScreen.integer == 7) { // 2018-10-05 fixed offsets @@ -7794,7 +7794,7 @@ static void CG_SetArmorColor (float alpha) { vec4_t color; - if (cgs.protocol != PROTOCOL_QL && !cgs.cpma) { + if (cgs.protocolClass != PROTOCOL_QL && !cgs.cpma) { return; } @@ -8529,7 +8529,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ } if (cg.snap->ps.pm_type == PM_INTERMISSION) { - if ( (cgs.protocol == PROTOCOL_QL && CG_IsDuelGame(cgs.gametype) && cg.duelForfeit) + if ( (cgs.protocolClass == PROTOCOL_QL && CG_IsDuelGame(cgs.gametype) && cg.duelForfeit) || (cgs.cpma && CG_IsDuelGame(cgs.gametype) && cg.duelForfeit) ) { @@ -9066,7 +9066,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ font = &cgDC.Assets.textFont; } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_timeoutcount"))) { CG_Text_Paint_Align(&rect, scale, color, va("TO: %s", CG_ConfigString(CS_RED_TEAM_TIMEOUTS_LEFT)), 0, 0, textStyle, font, align); } @@ -9078,7 +9078,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ font = &cgDC.Assets.textFont; } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (atoi(Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "g_timeoutcount"))) { CG_Text_Paint_Align(&rect, scale, color, va("TO: %s", CG_ConfigString(CS_BLUE_TEAM_TIMEOUTS_LEFT)), 0, 0, textStyle, font, align); } @@ -9295,7 +9295,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ font = &cgDC.Assets.textFont; } - if (cgs.protocol == PROTOCOL_QL && cg.duelForfeit && cg.duelPlayerForfeit == 1) { + if (cgs.protocolClass == PROTOCOL_QL && cg.duelForfeit && cg.duelPlayerForfeit == 1) { CG_Text_Paint_Align(&rect, scale, color, "-", 0, 0, textStyle, font, align); break; } @@ -9534,7 +9534,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ font = &cgDC.Assets.textFont; } - if ((cgs.protocol == PROTOCOL_QL && cg.duelForfeit && cg.duelPlayerForfeit == 2) || + if ((cgs.protocolClass == PROTOCOL_QL && cg.duelForfeit && cg.duelPlayerForfeit == 2) || /* with cpma we are always placing forfeiting player in second duel slot */ (cgs.cpma && cg.duelForfeit) ) { @@ -9972,7 +9972,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ } else { CG_Text_Paint_Align(&rect, scale, color, CG_ConfigString(CS_BEST_ITEM_CONTROL2), 0, 0, textStyle, font, align); } - } else if (cgs.protocol == PROTOCOL_QL) { + } else if (cgs.protocolClass == PROTOCOL_QL) { CG_Text_Paint_Align(&rect, scale, color, CG_ConfigString(CS_BEST_ITEM_CONTROL), 0, 0, textStyle, font, align); } break; @@ -9996,7 +9996,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ } else { CG_Text_Paint_Align(&rect, scale, color, CG_ConfigString(CS_MOST_ACCURATE2), 0, 0, textStyle, font, align); } - } else if (cgs.protocol == PROTOCOL_QL) { + } else if (cgs.protocolClass == PROTOCOL_QL) { CG_Text_Paint_Align(&rect, scale, color, CG_ConfigString(CS_MOST_ACCURATE), 0, 0, textStyle, font, align); } break; @@ -10020,7 +10020,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ } else { CG_Text_Paint_Align(&rect, scale, color, CG_ConfigString(CS_MOST_DAMAGE_DEALT2), 0, 0, textStyle, font, align); } - } else if (cgs.protocol == PROTOCOL_QL) { + } else if (cgs.protocolClass == PROTOCOL_QL) { CG_Text_Paint_Align(&rect, scale, color, CG_ConfigString(CS_MOST_DAMAGE_DEALT), 0, 0, textStyle, font, align); } break; @@ -10093,7 +10093,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ } } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { // 2018-07-17 ql shows hostname as part of message in menudef.h: // // "#define CG_MATCH_DETAILS 8" @@ -11256,7 +11256,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ ownerName = Info_ValueForKey(CG_ConfigString(CS_SERVERINFO), "sv_owner"); if (!ownerName || !*ownerName) { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { ownerName = "Quake Live"; } else { ownerName = "Quake 3"; @@ -11510,7 +11510,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ } else { Com_Printf("^3CG_MOST_VALUABLE_OFFENSIVE_PLYR invalid player number %d\n", n); } - } else if (cgs.protocol == PROTOCOL_QL) { + } else if (cgs.protocolClass == PROTOCOL_QL) { CG_Text_Paint_Align(&rect, scale, color, CG_ConfigString(CS_MVP_OFFENSE), 0, 0, textStyle, font, align); } break; @@ -11530,7 +11530,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ } else { Com_Printf("^3CG_MOST_VALUABLE_DEFENSIVE_PLYR invalid player number %d\n", n); } - } else if (cgs.protocol == PROTOCOL_QL) { + } else if (cgs.protocolClass == PROTOCOL_QL) { CG_Text_Paint_Align(&rect, scale, color, CG_ConfigString(CS_MVP_DEFENSE), 0, 0, textStyle, font, align); } break; @@ -11766,7 +11766,7 @@ void CG_OwnerDraw (float x, float y, float w, float h, float text_x, float text_ } case CG_PLAYER_HASKEY: { - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { break; } diff --git a/code/cgame/cg_players.c b/code/cgame/cg_players.c index 75a7381..c259ed6 100644 --- a/code/cgame/cg_players.c +++ b/code/cgame/cg_players.c @@ -1752,7 +1752,7 @@ void CG_NewClientInfo( int clientNum ) { } v = Info_ValueForKey(configstring, "c"); - if (*v && cgs.protocol == PROTOCOL_QL) { + if (*v && cgs.protocolClass == PROTOCOL_QL) { if (!Q_stricmp("n/a", v)) { newInfo.countryFlag = trap_R_RegisterShaderNoMip("ui/assets/flags/none.png"); } else if (*v) { @@ -1782,7 +1782,7 @@ void CG_NewClientInfo( int clientNum ) { // '2' ignores only for Quake Live demos if (cg_ignoreClientHeadModel.integer == 2) { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { v = model; } else { v = hmodel; @@ -1801,7 +1801,7 @@ void CG_NewClientInfo( int clientNum ) { // unedited model if (cg_ignoreClientHeadModel.integer == 2) { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { v = model; } else { v = hmodel; @@ -1929,7 +1929,7 @@ void CG_NewClientInfo( int clientNum ) { // head model if (cg_ignoreClientHeadModel.integer == 2) { - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { v = model; } else { v = hmodel; @@ -3598,7 +3598,7 @@ void CG_FloatEntNumbers (void) continue; } - if (cent->currentState.eType >= ET_EVENTS) { + if ((cgs.realProtocol >= 46 && cent->currentState.eType >= ET_EVENTS) || (cgs.realProtocol < 46 && cent->currentState.eType >= (ET_EVENTS - 1))) { //continue; } @@ -6642,7 +6642,7 @@ void CG_CheckForModelChange (const centity_t *cent, clientInfo_t *ci, refEntity_ return; } - if (cgs.protocol == PROTOCOL_QL && cgs.gametype == GT_RED_ROVER && cgs.customServerSettings & SERVER_SETTING_INFECTED && cg_playerModelAllowServerOverride.integer) { + if (cgs.protocolClass == PROTOCOL_QL && cgs.gametype == GT_RED_ROVER && cgs.customServerSettings & SERVER_SETTING_INFECTED && cg_playerModelAllowServerOverride.integer) { if (ci->team == TEAM_RED) { if (!Q_stricmp(ci->whiteName, "Infected Mastermind")) { CG_CopyClientInfoModel(&cgs.urielInfected, ci); diff --git a/code/cgame/cg_playerstate.c b/code/cgame/cg_playerstate.c index 10863dd..c07fce1 100644 --- a/code/cgame/cg_playerstate.c +++ b/code/cgame/cg_playerstate.c @@ -615,6 +615,8 @@ void CG_PushReward (sfxHandle_t sfx, qhandle_t shader, int rewardCount) { int i; + //Com_Printf("^3 reward %d\n", rewardCount); + if (cg_rewardsStack.integer == 0 && (cg.time - cg.rewardTime) < cg_drawRewardsTime.integer) { // uhmm, nice name for (i = (cg.rewardStack); i >= 0; i--) { //Com_Printf("%d: %d --> %d\n", i, shader, cg.rewardShader[i]); @@ -799,7 +801,7 @@ static void CG_CheckLocalSounds( const playerState_t *ps, const playerState_t *o //Com_Printf("^3%d\n", ps->generic1); - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { n = ps->generic1 / 64; } else if (cgs.cpma) { n = (ps->persistant[PERS_HITS] - ops->persistant[PERS_HITS]) / 26; @@ -844,7 +846,7 @@ static void CG_CheckLocalSounds( const playerState_t *ps, const playerState_t *o //FIXME // it's not based on info in peristant[], tested with godmode + bots // it's in ps->generic1 - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { n = ps->generic1 / 64; } else if (cgs.cpma) { n = (ps->persistant[PERS_HITS] - ops->persistant[PERS_HITS]) / 26; @@ -883,7 +885,7 @@ static void CG_CheckLocalSounds( const playerState_t *ps, const playerState_t *o //FIXME // it's not based on info in peristant[], tested with godmode + bots // it's in ps->generic1 - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { n = ps->generic1 / 64; } else if (cgs.cpma) { n = (ps->persistant[PERS_HITS] - ops->persistant[PERS_HITS]) / 26; @@ -917,7 +919,7 @@ static void CG_CheckLocalSounds( const playerState_t *ps, const playerState_t *o #endif } else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) { if ((!wolfcam_following || (wolfcam_following && wcg.clientNum == cg.snap->ps.clientNum)) && !cg.freecam && !cg.cameraMode) { - if (cg_hitBeep.integer && cgs.protocol == PROTOCOL_QL) { + if (cg_hitBeep.integer && cgs.protocolClass == PROTOCOL_QL) { CG_StartLocalSound( cgs.media.hitTeamSound, CHAN_LOCAL_SOUND ); } } @@ -956,11 +958,15 @@ static void CG_CheckLocalSounds( const playerState_t *ps, const playerState_t *o cg.rewardAssist = qfalse; #endif - if (ps->persistant[PERS_CAPTURES] > ops->persistant[PERS_CAPTURES] && cg_rewardCapture.integer) { - CG_PushReward(cgs.media.captureAwardSound, cgs.media.medalCapture, ps->persistant[PERS_CAPTURES]); - reward = qtrue; - //Com_Printf("capture\n"); + // not in protocol 46 + if (cgs.realProtocol >= 48) { + if (ps->persistant[PERS_CAPTURES] > ops->persistant[PERS_CAPTURES] && cg_rewardCapture.integer) { + CG_PushReward(cgs.media.captureAwardSound, cgs.media.medalCapture, ps->persistant[PERS_CAPTURES]); + reward = qtrue; + //Com_Printf("capture\n"); + } } + if (ps->persistant[PERS_IMPRESSIVE_COUNT] > ops->persistant[PERS_IMPRESSIVE_COUNT] && cg_rewardImpressive.integer) { //cg.rewardImpressive = qtrue; //Com_Printf("^3impressive\n"); @@ -1010,20 +1016,24 @@ static void CG_CheckLocalSounds( const playerState_t *ps, const playerState_t *o reward = qtrue; //Com_Printf("gauntlet frag\n"); } - if (ps->persistant[PERS_DEFEND_COUNT] > ops->persistant[PERS_DEFEND_COUNT] && cg_rewardDefend.integer) { - //cg.rewardDefend = qtrue; - CG_PushReward(cgs.media.defendSound, cgs.media.medalDefend, ps->persistant[PERS_DEFEND_COUNT]); - reward = qtrue; - //Com_Printf("defend\n"); - } - if (ps->persistant[PERS_ASSIST_COUNT] > ops->persistant[PERS_ASSIST_COUNT] && cg_rewardAssist.integer) { - //cg.rewardAssist = qtrue; + if (cgs.realProtocol >= 46) { + if (ps->persistant[PERS_DEFEND_COUNT] > ops->persistant[PERS_DEFEND_COUNT] && cg_rewardDefend.integer) { + //cg.rewardDefend = qtrue; - CG_PushReward(cgs.media.assistSound, cgs.media.medalAssist, ps->persistant[PERS_ASSIST_COUNT]); - reward = qtrue; - //Com_Printf("assist\n"); + CG_PushReward(cgs.media.defendSound, cgs.media.medalDefend, ps->persistant[PERS_DEFEND_COUNT]); + reward = qtrue; + //Com_Printf("defend\n"); + } + if (ps->persistant[PERS_ASSIST_COUNT] > ops->persistant[PERS_ASSIST_COUNT] && cg_rewardAssist.integer) { + //cg.rewardAssist = qtrue; + //Com_Printf("^3 assist\n"); + CG_PushReward(cgs.media.assistSound, cgs.media.medalAssist, ps->persistant[PERS_ASSIST_COUNT]); + reward = qtrue; + //Com_Printf("assist\n"); + } } + // if any of the player event bits changed //Com_Printf("%d: %d %d -> %d client %d %d\n", cg.time, PERS_PLAYEREVENTS, ops->persistant[PERS_PLAYEREVENTS], ps->persistant[PERS_PLAYEREVENTS], ops->clientNum, ps->clientNum); @@ -1258,7 +1268,7 @@ static void test_persStats (const playerState_t *ps) { int i; - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { for (i = STAT_MAX_HEALTH + 1; i < 16; i++) { if (ps->stats[i]) { if (SC_Cvar_Get_Int("cg_debugstats")) { diff --git a/code/cgame/cg_scoreboard.c b/code/cgame/cg_scoreboard.c index cd6f876..1db8a42 100644 --- a/code/cgame/cg_scoreboard.c +++ b/code/cgame/cg_scoreboard.c @@ -349,7 +349,7 @@ static int CG_TeamScoreboard( int y, team_t team, float fade, int maxClients, in } } - if (cgs.protocol == PROTOCOL_QL && CG_IsDuelGame(cgs.gametype) && cg.snap->ps.pm_type == PM_INTERMISSION && cg.duelForfeit) { + if (cgs.protocolClass == PROTOCOL_QL && CG_IsDuelGame(cgs.gametype) && cg.snap->ps.pm_type == PM_INTERMISSION && cg.duelForfeit) { if (team == TEAM_FREE) { CG_DrawClientScoreQlForfeit(y + lineHeight * count, 0, fade); count++; diff --git a/code/cgame/cg_servercmds.c b/code/cgame/cg_servercmds.c index 3d1fe8c..23cbd85 100644 --- a/code/cgame/cg_servercmds.c +++ b/code/cgame/cg_servercmds.c @@ -130,8 +130,13 @@ static void CG_ParseScores( void ) { cg.avgBluePing = 0; //memset( cg.scores, 0, sizeof( cg.scores ) ); - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { SCSIZE = 18; + } else if (cgs.realProtocol < 46) { + SCSIZE = 6; + } else if (cgs.realProtocol == 46) { + //FIXME some team arena stuff enabled? + SCSIZE = 15; } else { SCSIZE = 14; } @@ -149,20 +154,29 @@ static void CG_ParseScores( void ) { cg.scores[i].time = atoi( CG_Argv( i * SCSIZE + 7 ) ); cg.scores[i].scoreFlags = atoi( CG_Argv( i * SCSIZE + 8 ) ); cg.scores[i].powerups = atoi( CG_Argv( i * SCSIZE + 9 ) ); - cg.scores[i].accuracy = atoi(CG_Argv(i * SCSIZE + 10)); - cg.scores[i].impressiveCount = atoi(CG_Argv(i * SCSIZE + 11)); - cg.scores[i].excellentCount = atoi(CG_Argv(i * SCSIZE + 12)); - cg.scores[i].gauntletCount = atoi(CG_Argv(i * SCSIZE + 13)); - cg.scores[i].defendCount = atoi(CG_Argv(i * SCSIZE + 14)); - cg.scores[i].assistCount = atoi(CG_Argv(i * SCSIZE + 15)); - cg.scores[i].perfect = atoi(CG_Argv(i * SCSIZE + 16)); - cg.scores[i].captures = atoi(CG_Argv(i * SCSIZE + 17)); - - if (cgs.protocol == PROTOCOL_QL) { - cg.scores[i].alive = atoi(CG_Argv(i * SCSIZE + 18)); - cg.scores[i].frags = atoi(CG_Argv(i * SCSIZE + 19)); - cg.scores[i].deaths = atoi(CG_Argv(i * SCSIZE + 20)); - cg.scores[i].bestWeapon = atoi(CG_Argv(i * SCSIZE + 21)); + + if (cgs.realProtocol >= 46) { + cg.scores[i].accuracy = atoi(CG_Argv(i * SCSIZE + 10)); + cg.scores[i].impressiveCount = atoi(CG_Argv(i * SCSIZE + 11)); + cg.scores[i].excellentCount = atoi(CG_Argv(i * SCSIZE + 12)); + cg.scores[i].gauntletCount = atoi(CG_Argv(i * SCSIZE + 13)); + cg.scores[i].defendCount = atoi(CG_Argv(i * SCSIZE + 14)); + cg.scores[i].assistCount = atoi(CG_Argv(i * SCSIZE + 15)); + cg.scores[i].perfect = atoi(CG_Argv(i * SCSIZE + 16)); + cg.scores[i].captures = atoi(CG_Argv(i * SCSIZE + 17)); + +#if 0 + //FIXME what is the last one for protocol 46? + // 2024-09-08 so far only seen it set to 0, maybe accidentally enabled team area stuff + Com_Printf(" scores clientNum %d : SC-15 %d\n", clientNum, atoi(CG_Argv(i * SCSIZE + 18))); +#endif + + if (cgs.protocolClass == PROTOCOL_QL) { + cg.scores[i].alive = atoi(CG_Argv(i * SCSIZE + 18)); + cg.scores[i].frags = atoi(CG_Argv(i * SCSIZE + 19)); + cg.scores[i].deaths = atoi(CG_Argv(i * SCSIZE + 20)); + cg.scores[i].bestWeapon = atoi(CG_Argv(i * SCSIZE + 21)); + } } //Com_Printf("score %d %s\n", i, cgs.clientinfo[cg.scores[i].client].name); @@ -1048,6 +1062,29 @@ static void CG_ParseTeamInfo( void ) { 0.1.0.263 0.1.0.258 0.1.0.256 QuakeLive 0.1.0.256 linux-i386 Aug 10 2009 19:56:10 + + Quake 3: + + version\Q3 1.27h win-x86 Jan 8 2001 -- protocol 48 + version\Q3 1.27g linux-i386 Dec 18 2000 -- protocol 48 + + version\Q3 1.25y win-x86 Oct 12 2000 -- protocol 46 + + version\Q3 1.25v win-x86 Oct 9 2000 -- protocol 46 + + version\Q3 1.25p win-x86 Sep 22 2000 -- protocol 46 + + version\Q3 1.17 win-x86 Apr 27 2000 -- protocol 45 + + https://discourse.ioquake.org/t/quake-3-changelog-version-history/375 + + * To help facilitate a rapid transition to the new codebase we have also bumped the network protocol version [to protocol 45]. This means 1.17 is not network compatibile with any prior version. + + version\Q3 1.16n win-x86 Mar 14 2000 -- protocol 43 + + version\Q3 1.15c win-x86 Jan 13 2000 -- protocol 43 + + version\Q3 1.09 win-x86 Nov 12 1999 -- protocol 43 */ #define BUFFER_LENGTH 1024 @@ -1290,7 +1327,7 @@ void CG_LoadInfectedGameTypeModels (void) static void CG_SetScorePlace (void) { - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } @@ -1358,7 +1395,7 @@ void CG_ParseServerinfo (qboolean firstCall, qboolean seeking) //FIXME PROTOCOL_Q3 cgs.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) ); - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { if (cgs.cpma) { // done in CG_CpmaParseGameState() } else if (cgs.q3plus) { @@ -1430,7 +1467,7 @@ The one who kills the leader gets the new leader and automatically is hunted by break; } } - } else if (cgs.protocol == PROTOCOL_QL) { + } else if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.gametype == 2) { cgs.gametype = GT_RACE; } @@ -1459,7 +1496,7 @@ The one who kills the leader gets the new leader and automatically is hunted by cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) ); cgs.realTimelimit = cgs.timelimit; - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { str = CG_ConfigString(CS_ROUND_STATUS); cgs.roundNum = atoi(Info_ValueForKey(str, "round")); cgs.roundTurn = atoi(Info_ValueForKey(str, "turn")); @@ -1573,7 +1610,7 @@ The one who kills the leader gets the new leader and automatically is hunted by Q_strncpyz(cgs.voteString, CG_ConfigString(CS_VOTE_STRING), sizeof(cgs.voteString)); //FIXME team votes - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { //FIXME team vote not seen yet in ql } else { cgs.teamVoteTime[0] = atoi(CG_ConfigStringNoConvert(CSQ3_TEAMVOTE_TIME + 0)); @@ -1589,7 +1626,7 @@ The one who kills the leader gets the new leader and automatically is hunted by } } - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { if (firstCall) { Q_strncpyz(cgs.redTeamClanTag, "Red Team", sizeof(cgs.redTeamClanTag)); Q_strncpyz(cgs.redTeamName, "Red Team", sizeof(cgs.redTeamName)); @@ -1633,7 +1670,7 @@ The one who kills the leader gets the new leader and automatically is hunted by } } - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } @@ -1682,7 +1719,7 @@ The one who kills the leader gets the new leader and automatically is hunted by //FIXME breaks with seeking if ( - (cgs.protocol == PROTOCOL_QL && cgs.realProtocol <= 90) && + (cgs.protocolClass == PROTOCOL_QL && cgs.realProtocol <= 90) && (cgs.lastConnectedDisconnectedPlayer > -1 && cgs.lastConnectedDisconnectedPlayerName[0] && cgs.lastConnectedDisconnectedPlayerClientInfo && cgs.needToCheckSkillRating) ) { for (i = 0, numClients = 0; i < MAX_CLIENTS; i++) { @@ -1802,7 +1839,7 @@ void CG_ParseWarmup( void ) { if (cgs.cpma) { info = CG_ConfigStringNoConvert(CSCPMA_GAMESTATE); warmup = atoi(Info_ValueForKey(info, "tw")); - } else if (cgs.protocol == PROTOCOL_QL) { + } else if (cgs.protocolClass == PROTOCOL_QL) { info = CG_ConfigString( CS_WARMUP ); warmup = atoi(Info_ValueForKey(info, "time")); } else { @@ -1862,7 +1899,7 @@ Called on load to set the initial values from configure strings void CG_SetConfigValues( void ) { const char *s; - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { cgs.scores1 = atoi( CG_ConfigString( CS_SCORES1 ) ); cgs.scores2 = atoi( CG_ConfigString( CS_SCORES2 ) ); } else if (!cgs.cpma) { @@ -1882,7 +1919,7 @@ void CG_SetConfigValues( void ) { } #endif - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.realProtocol < 91) { cgs.dominationRedPoints = atoi(CG_ConfigStringNoConvert(CS_DOMINATION_RED_POINTS)); cgs.dominationBluePoints = atoi(CG_ConfigStringNoConvert(CS_DOMINATION_BLUE_POINTS)); @@ -1896,7 +1933,7 @@ void CG_SetConfigValues( void ) { } //FIXME GT_1FCTF see cg_servercmds.c - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { cgs.redPlayersLeft = atoi(CG_ConfigString(CS_RED_PLAYERS_LEFT)); cgs.bluePlayersLeft = atoi(CG_ConfigString(CS_BLUE_PLAYERS_LEFT)); } else { @@ -1904,7 +1941,7 @@ void CG_SetConfigValues( void ) { cgs.bluePlayersLeft = 0; } - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } @@ -2019,7 +2056,7 @@ void CG_InterMissionHit (void) static qboolean CG_IsModelConfigString (int num, int *modelNum) { - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { if (num >= CSQ3_MODELS && num < CSQ3_MODELS + MAX_MODELS) { *modelNum = num - CSQ3_MODELS; return qtrue; @@ -2036,7 +2073,7 @@ static qboolean CG_IsModelConfigString (int num, int *modelNum) static qboolean CG_IsSoundConfigString (int num, int *soundNum) { - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { if (num >= CSQ3_SOUNDS && num < CSQ3_SOUNDS + MAX_SOUNDS) { *soundNum = num - CSQ3_SOUNDS; return qtrue; @@ -2053,7 +2090,7 @@ static qboolean CG_IsSoundConfigString (int num, int *soundNum) static qboolean CG_IsPlayerConfigString (int num, int *clientNum) { - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { if (num >= CSQ3_PLAYERS && num < CSQ3_PLAYERS + MAX_CLIENTS) { *clientNum = num - CSQ3_PLAYERS; return qtrue; @@ -2071,10 +2108,17 @@ static qboolean CG_IsPlayerConfigString (int num, int *clientNum) #if 0 static qboolean CG_IsLocationConfigString (int num, int *locationNum) { - if (cgs.protocol == PROTOCOL_Q3) { - if (num >= CSQ3_LOCATIONS && num < CSQ3_LOCATIONS + MAX_LOCATIONS) { - *locationNum = num - CSQ3_LOCATIONS; - return qtrue; + if (cgs.protocolClass == PROTOCOL_Q3) { + if (cgs.realProtocol < 46) { + if (num >= CSQ3DM3_LOCATIONS && num < CSQ3DM3_LOCATIONS + MAX_LOCATIONS) { + *locationNum = num - CSQ3DM3_LOCATIONS; + return qtrue; + } + } else { + if (num >= CSQ3_LOCATIONS && num < CSQ3_LOCATIONS + MAX_LOCATIONS) { + *locationNum = num - CSQ3_LOCATIONS; + return qtrue; + } } } else { if (num >= CS_LOCATIONS && num < CS_PARTICLES) { @@ -2089,7 +2133,7 @@ static qboolean CG_IsLocationConfigString (int num, int *locationNum) static qboolean CG_IsTeamVoteTime (int num, int *newNum) { - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { if (num >= CSQ3_TEAMVOTE_TIME && num <= CSQ3_TEAMVOTE_TIME + 1) { *newNum = num - CSQ3_TEAMVOTE_TIME; return qtrue; @@ -2106,7 +2150,7 @@ static qboolean CG_IsTeamVoteTime (int num, int *newNum) static qboolean CG_IsTeamVoteYes (int num, int *newNum) { - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { if (num >= CSQ3_TEAMVOTE_YES && num <= CSQ3_TEAMVOTE_YES + 1) { *newNum = num - CSQ3_TEAMVOTE_YES; return qtrue; @@ -2123,7 +2167,7 @@ static qboolean CG_IsTeamVoteYes (int num, int *newNum) static qboolean CG_IsTeamVoteNo (int num, int *newNum) { - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { if (num >= CSQ3_TEAMVOTE_NO && num <= CSQ3_TEAMVOTE_NO + 1) { *newNum = num - CSQ3_TEAMVOTE_NO; return qtrue; @@ -2140,7 +2184,7 @@ static qboolean CG_IsTeamVoteNo (int num, int *newNum) static qboolean CG_IsTeamVoteString (int num, int *newNum) { - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { if (num >= CSQ3_TEAMVOTE_STRING && num <= CSQ3_TEAMVOTE_STRING + 1) { *newNum = num - CSQ3_TEAMVOTE_STRING; return qtrue; @@ -2489,7 +2533,7 @@ static void CG_ConfigStringModified( void ) { Q_strncpyz(buf, str, sizeof(buf)); - if (cgs.protocol == PROTOCOL_Q3) { + if (cgs.protocolClass == PROTOCOL_Q3) { CG_ConfigStringIndexFromQ3(&num); if (SC_Cvar_Get_Int("debug_configstring")) { Com_Printf("%d --> %d\n", numOrig, num); @@ -2516,13 +2560,13 @@ static void CG_ConfigStringModified( void ) { CG_ParseWarmup(); } else if ( num == CS_SCORES1 && !cgs.cpma) { cgs.scores1 = atoi( str ); - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { cgs.firstPlace[0] = '\0'; cgs.secondPlace[0] = '\0'; } } else if ( num == CS_SCORES2 && !cgs.cpma) { cgs.scores2 = atoi( str ); - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { cgs.firstPlace[0] = '\0'; cgs.secondPlace[0] = '\0'; } @@ -2570,7 +2614,7 @@ static void CG_ConfigStringModified( void ) { } //FIXME quakelive has qtrue for intermission - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (*str) { cg.intermissionStarted = qtrue; } else { @@ -2631,7 +2675,7 @@ static void CG_ConfigStringModified( void ) { else if ( num == CS_SHADERSTATE ) { CG_ShaderStateChanged(); - } else if (cgs.protocol == PROTOCOL_Q3) { + } else if (cgs.protocolClass == PROTOCOL_Q3) { if (cgs.cpma) { if (CG_CpmaCs(num)) { return; @@ -3568,7 +3612,7 @@ static void CG_RemoveChatEscapeChar( char *text ) { int len; char orig[MAX_STRING_CHARS]; - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { return; } @@ -4259,7 +4303,7 @@ static void CG_ParseScores_Ffa (void) cg.avgBluePing = 0; //memset( cg.scores, 0, sizeof( cg.scores ) ); - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { SCSIZE = 18; } else { SCSIZE = 14; @@ -5635,7 +5679,7 @@ static void CG_ServerCommand( void ) { CG_StartLocalSound( cgs.media.votePassed, CHAN_ANNOUNCER ); } else if (!Q_stricmpn(cmd, "team vote passed", 16) && cg_audioAnnouncerTeamVote.integer) { CG_StartLocalSound( cgs.media.votePassed, CHAN_ANNOUNCER ); - } else if (cgs.protocol == PROTOCOL_QL && CG_IsDuelGame(cgs.gametype) && !Q_stricmpn(cmd, "Game has been forfeited", 23)) { + } else if (cgs.protocolClass == PROTOCOL_QL && CG_IsDuelGame(cgs.gametype) && !Q_stricmpn(cmd, "Game has been forfeited", 23)) { //FIXME bad hack Com_Printf("^5forfeit...\n"); cg.duelForfeit = qtrue; @@ -5866,7 +5910,7 @@ static void CG_ServerCommand( void ) { } } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { // crash tutorial if (!Q_stricmp(cmd, "playSound")) { if (!cg_ignoreServerPlaySound.integer) { diff --git a/code/cgame/cg_snapshot.c b/code/cgame/cg_snapshot.c index 7507092..b1fb9ed 100644 --- a/code/cgame/cg_snapshot.c +++ b/code/cgame/cg_snapshot.c @@ -279,7 +279,7 @@ static void CG_TransitionSnapshot( void ) { //Com_Printf("missile %d pos.trTime %d server time %d %d %s\n", cg.snap->entities[i].number, cg.snap->entities[i].pos.trTime, cg.snap->serverTime, cg.snap->serverTime - cg.snap->entities[i].pos.trTime, weapNames[cg.snap->entities[i].weapon]); } - if (cgs.protocol == PROTOCOL_QL && cg.snap->entities[i].eType == ET_ITEM) { + if (cgs.protocolClass == PROTOCOL_QL && cg.snap->entities[i].eType == ET_ITEM) { const entityState_t *es = &cg.snap->entities[i]; // check if we can update client item timers with ingame timer // pie info @@ -380,7 +380,7 @@ static void CG_TransitionSnapshot( void ) { } // set first and second place names if possible - if (cgs.protocol != PROTOCOL_QL) { + if (cgs.protocolClass != PROTOCOL_QL) { if (CG_IsDuelGame(cgs.gametype)) { if (cgs.clientinfo[cg.snap->ps.clientNum].team != TEAM_SPECTATOR) { int otherDuelPlayer; @@ -842,7 +842,7 @@ void CG_ResetTimeChange (int serverTime, int ioverf) cgs.levelStartTime = atoi(CG_ConfigString(CS_LEVEL_START_TIME)); cg.intermissionStarted = atoi(CG_ConfigString(CS_INTERMISSION)); - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { int roundTime; //Com_Printf("^2 round status: '%s'\n", CG_ConfigString(CS_ROUND_STATUS)); @@ -870,7 +870,7 @@ void CG_ResetTimeChange (int serverTime, int ioverf) cgs.roundNum = 0; } - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { cgs.timeoutBeginTime = atoi(CG_ConfigString(CS_TIMEOUT_BEGIN_TIME)); cgs.timeoutEndTime = atoi(CG_ConfigString(CS_TIMEOUT_END_TIME)); cgs.timeoutCountingDown = qfalse; @@ -1291,7 +1291,7 @@ void CG_ResetTimeChange (int serverTime, int ioverf) } cgs.thirtySecondWarningPlayed = qfalse; - if (cgs.roundStarted && cgs.protocol == PROTOCOL_QL) { + if (cgs.roundStarted && cgs.protocolClass == PROTOCOL_QL) { ival = cg.time - atoi(CG_ConfigString(CS_ROUND_TIME)); if (cgs.roundtimelimit - (ival / 1000) < 30) { cgs.thirtySecondWarningPlayed = qtrue; @@ -1371,7 +1371,7 @@ void CG_ResetTimeChange (int serverTime, int ioverf) // get alive status for scoreboard - if (cgs.protocol == PROTOCOL_QL || cgs.cpma) { + if (cgs.protocolClass == PROTOCOL_QL || cgs.cpma) { int roundStartTime = 0; for (i = cg.numRoundStarts - 1; i >= 0; i--) { diff --git a/code/cgame/cg_view.c b/code/cgame/cg_view.c index 1922166..1a46a67 100644 --- a/code/cgame/cg_view.c +++ b/code/cgame/cg_view.c @@ -1496,7 +1496,7 @@ static int CG_CalcViewValues( void ) { // back away from character // hack for ql setting spec health to 0, don't use third person offset - if (cgs.protocol == PROTOCOL_QL && cg.snap->ps.clientNum == cg.clientNum && cg.snap->ps.stats[STAT_HEALTH] <= 0 && cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) { + if (cgs.protocolClass == PROTOCOL_QL && cg.snap->ps.clientNum == cg.clientNum && cg.snap->ps.stats[STAT_HEALTH] <= 0 && cg.snap->ps.persistant[PERS_TEAM] == TEAM_SPECTATOR) { if (cg_specOffsetQL.integer == 1) { CG_OffsetQuakeLiveSpec(); } else if (cg_specOffsetQL.integer == 2) { @@ -4713,7 +4713,7 @@ static void CG_FreeCam (void) static void CG_CheckSkillRating (void) { - if (!cgs.needToCheckSkillRating || cgs.protocol != PROTOCOL_QL) { + if (!cgs.needToCheckSkillRating || cgs.protocolClass != PROTOCOL_QL) { return; } @@ -6064,7 +6064,7 @@ void CG_DrawActiveFrame (int serverTime, stereoFrame_t stereoView, qboolean demo useClientNum = qfalse; // only ql sets owner (otherEntityNum) for missiles - if (cg_autoChaseMissile.integer == 1 && cgs.protocol == PROTOCOL_QL) { + if (cg_autoChaseMissile.integer == 1 && cgs.protocolClass == PROTOCOL_QL) { useClientNum = qtrue; } else { // use any missile @@ -6176,10 +6176,14 @@ void CG_DrawActiveFrame (int serverTime, stereoFrame_t stereoView, qboolean demo #if 0 Com_Printf("exCount:%d defendCount:%d assistCount:%d gauntFragCount:%d attArmor:%d\n", cg.snap->ps.persistant[PERS_EXCELLENT_COUNT], + // not in protocol 43 cg.snap->ps.persistant[PERS_DEFEND_COUNT], + // not in protocol 43 cg.snap->ps.persistant[PERS_ASSIST_COUNT], cg.snap->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], + // not in protocol 43 cg.snap->ps.persistant[PERS_CAPTURES], + // not in protocol 43 cg.snap->ps.persistant[PERS_ATTACKEE_ARMOR]); #endif diff --git a/code/cgame/sc_misc.c b/code/cgame/sc_misc.c index f386853..624c067 100644 --- a/code/cgame/sc_misc.c +++ b/code/cgame/sc_misc.c @@ -923,7 +923,7 @@ qboolean CG_GameTimeout (void) { qboolean inTimeout = qfalse; - if (cgs.protocol == PROTOCOL_QL) { + if (cgs.protocolClass == PROTOCOL_QL) { if (cgs.timeoutBeginTime) { if (!cgs.timeoutEndTime) { // ref pause inTimeout = qtrue; @@ -1202,7 +1202,7 @@ qboolean CG_CpmaIsRoundWarmup (void) */ qboolean CG_IsCpmaMvd (void) { - if (cgs.protocol != PROTOCOL_Q3 || !cgs.cpma) { + if (cgs.protocolClass != PROTOCOL_Q3 || !cgs.cpma) { return qfalse; } diff --git a/code/cgame/wolfcam_ents.c b/code/cgame/wolfcam_ents.c index 55149d3..9ed4035 100644 --- a/code/cgame/wolfcam_ents.c +++ b/code/cgame/wolfcam_ents.c @@ -423,11 +423,11 @@ qboolean Wolfcam_InterpolateEntityPosition (centity_t *cent) } } - if (ping > 80 && cgs.protocol == PROTOCOL_QL) { + if (ping > 80 && cgs.protocolClass == PROTOCOL_QL) { ping = 80; } - if (cgs.protocol == PROTOCOL_Q3 && !cgs.cpma) { + if (cgs.protocolClass == PROTOCOL_Q3 && !cgs.cpma) { ping = 0; // no antilag } diff --git a/code/client/cl_cgame.c b/code/client/cl_cgame.c index bbeb50e..7200b2c 100644 --- a/code/client/cl_cgame.c +++ b/code/client/cl_cgame.c @@ -345,7 +345,11 @@ qboolean CL_PeekSnapshot (int snapshotNumber, snapshot_t *snapshot) // init the message memset(&buf, 0, sizeof(msg_t)); - MSG_Init(&buf, bufData, sizeof(bufData)); + if (di.olderUncompressedDemo) { + MSG_InitOOB(&buf, bufData, sizeof(bufData)); + } else { + MSG_Init(&buf, bufData, sizeof(bufData)); + } // get the length r = FS_Read (&buf.cursize, 4, clc.demoReadFile); @@ -401,10 +405,18 @@ qboolean CL_PeekSnapshot (int snapshotNumber, snapshot_t *snapshot) buf.readcount = 0; // CL_ParseServerMessage( &buf ); - MSG_Bitstream(&buf); + if (!di.olderUncompressedDemo) { + MSG_Bitstream(&buf); + } + // get the reliable sequence acknowledge number //clc.reliableAcknowledge = MSG_ReadLong( msg ); - MSG_ReadLong(&buf); + // protocol 43 doesn't have this + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol < 46) { + // pass + } else { + MSG_ReadLong(&buf); + } // // parse the message @@ -425,8 +437,22 @@ qboolean CL_PeekSnapshot (int snapshotNumber, snapshot_t *snapshot) break; } + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48) { + if (buf.readcount == buf.cursize) { + //SHOWNET( msg, "END OF MESSAGE" ); + break; + } + } + cmd = MSG_ReadByte(&buf); +#if 0 // testing voip with protocol 43 + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol < 48) { + if (cmd == svc_bad) { + break; + } + } +#endif // See if this is an extension command after the EOF, which means we // have speex voip data. if ((cmd == svc_EOF) && (MSG_LookaheadByte( &buf ) == svc_extension)) { @@ -447,7 +473,7 @@ qboolean CL_PeekSnapshot (int snapshotNumber, snapshot_t *snapshot) switch (cmd) { default: - Com_Error (ERR_DROP,"CL_PeekSnapshot: Illegible server message"); + Com_Error (ERR_DROP,"CL_PeekSnapshot: Illegible server message %d", cmd); break; case svc_nop: break; @@ -504,6 +530,19 @@ qboolean CL_PeekSnapshot (int snapshotNumber, snapshot_t *snapshot) break; #endif } + + //Com_Printf("look ahead1 %d\n", MSG_LookaheadByte( &buf )); + + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48) { + // _inMsg.GoToNextByte(); + //Com_Printf("nextbyte %d %d\n", msg->readcount, msg->bit); + if ((buf.bit & 7) != 0) { + buf.readcount++; + buf.bit = buf.readcount << 3; + } + } + + //Com_Printf("look ahead2 %d\n", MSG_LookaheadByte( &buf )); } // while (1) reading commands alldone: diff --git a/code/client/cl_main.c b/code/client/cl_main.c index c4cbe3d..8da07d8 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -219,6 +219,7 @@ static void CL_CheckWorkshopDownload (void); void CL_StopVideo_f (void); static void stream_demo_look_ahead (void); +static qboolean check_for_older_uncompressed_demo (void); /* =============== @@ -791,14 +792,22 @@ void CL_Record_f( void ) { if ( Cmd_Argc() == 2 ) { s = Cmd_Argv(1); Q_strncpyz( demoName, s, sizeof( demoName ) ); - Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, clc.realProtocol); + if (clc.realProtocol < 48) { + Com_sprintf(name, sizeof(name), "demos/%s.dm3", demoName); + } else { + Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, clc.realProtocol); + } } else { int number; // scan for a free demo name for ( number = 0 ; number <= 9999 ; number++ ) { CL_DemoFilename( number, demoName, sizeof( demoName ) ); - Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, clc.realProtocol); + if (clc.realProtocol < 48) { + Com_sprintf(name, sizeof(name), "demos/%s.dm3", demoName); + } else { + Com_sprintf(name, sizeof(name), "demos/%s.%s%d", demoName, DEMOEXT, clc.realProtocol); + } if (!FS_FileExists(name)) break; // file doesn't exist @@ -826,11 +835,19 @@ void CL_Record_f( void ) { clc.demowaiting = qtrue; // write out the gamestate message - MSG_Init (&buf, bufData, sizeof(bufData)); - MSG_Bitstream(&buf); + if (clc.realProtocol <= 48) { + MSG_InitOOB(&buf, bufData, sizeof(bufData)); + } else { + MSG_Init (&buf, bufData, sizeof(bufData)); + MSG_Bitstream(&buf); + } // NOTE, MRE: all server->client messages now acknowledge - MSG_WriteLong( &buf, clc.reliableSequence ); + // protocol 43 doesn't have this + //FIXME what about 44, 45 ? + if (clc.realProtocol > 43) { + MSG_WriteLong( &buf, clc.reliableSequence ); + } MSG_WriteByte (&buf, svc_gamestate); MSG_WriteLong (&buf, clc.serverCommandSequence ); @@ -844,6 +861,7 @@ void CL_Record_f( void ) { MSG_WriteByte (&buf, svc_configstring); MSG_WriteShort (&buf, i); MSG_WriteBigString (&buf, s); + //Com_Printf("%d: '%s'\n", i, s); } // baselines @@ -855,19 +873,38 @@ void CL_Record_f( void ) { } MSG_WriteByte (&buf, svc_baseline); MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue ); + //Com_Printf("wrote ent %d\n", i); } - MSG_WriteByte( &buf, svc_EOF ); + if (clc.realProtocol > 48) { + MSG_WriteByte( &buf, svc_EOF ); + } // finished writing the gamestate stuff - // write the client num - MSG_WriteLong(&buf, clc.clientNum); - // write the checksum feed - MSG_WriteLong(&buf, clc.checksumFeed); + //FIXME protocol >= 66 + if (clc.realProtocol <= 48) { + // pass, no clientNum or checksumFeed + //FIXME is this right? -- 2024-08-22 yes + MSG_WriteByte( &buf, svc_bad ); + //MSG_WriteByte( &buf, svc_EOF ); +#if 1 + if ((buf.bit & 7) != 0) { + buf.cursize++; + buf.bit = buf.cursize << 3; + } +#endif + //MSG_WriteByte( &buf, svc_EOF ); + } else { + // write the client num + MSG_WriteLong(&buf, clc.clientNum); + // write the checksum feed + MSG_WriteLong(&buf, clc.checksumFeed); + + // finished writing the client packet + MSG_WriteByte( &buf, svc_EOF ); + } - // finished writing the client packet - MSG_WriteByte( &buf, svc_EOF ); // write it to the demo file len = LittleLong( clc.serverMessageSequence - 1 ); @@ -1013,20 +1050,37 @@ static void CL_WriteNonDeltaDemoMessage (msg_t *inMsg) playerState_t tmpps; qboolean haveSnapshot = qfalse; - MSG_BeginReading(inMsg); - - MSG_Init(&outMsg, data, sizeof(data)); - MSG_Bitstream(&outMsg); + if (clc.realProtocol <= 48) { + MSG_BeginReadingOOB(inMsg); + MSG_InitOOB(&outMsg, data, sizeof(data)); + } else { + MSG_BeginReading(inMsg); + MSG_Init(&outMsg, data, sizeof(data)); + MSG_Bitstream(&outMsg); + } // CL_ParseServerMessage // reliable seq ack - r = MSG_ReadLong(inMsg); - MSG_WriteLong(&outMsg, r); + // NOTE, MRE: all server->client messages now acknowledge + // protocol 43 doesn't have this + //FIXME what about 44, 45 ? + if (clc.realProtocol >= 46) { + r = MSG_ReadLong(inMsg); + MSG_WriteLong(&outMsg, r); + } while (1) { qboolean dataFollowsEOF = qfalse; + if (clc.realProtocol <= 48) { + if (inMsg->readcount == inMsg->cursize) { + // "END OF MESSAGE" + //Com_Printf("wrnd END OF MESSAGE\n"); + break; + } + } + cmd = MSG_ReadByte(inMsg); MSG_WriteByte(&outMsg, cmd); @@ -1048,7 +1102,7 @@ static void CL_WriteNonDeltaDemoMessage (msg_t *inMsg) break; } - //Com_Printf("cmd: %d\n", cmd); + //Com_Printf("wrnd cmd: %d (%d %d)\n", cmd, inMsg->readcount, inMsg->cursize); switch ( cmd ) { default: @@ -1093,13 +1147,23 @@ static void CL_WriteNonDeltaDemoMessage (msg_t *inMsg) } } - // client num - r = MSG_ReadLong(inMsg); - MSG_WriteLong(&outMsg, r); + //FIXME protocol >= 66 + if (clc.realProtocol <= 48) { + // pass, no clientNum or checksumFeed + //FIXME is this right? + if ((outMsg.bit & 7) != 0) { + outMsg.cursize++; + outMsg.bit = outMsg.cursize << 3; + } + } else { + // client num + r = MSG_ReadLong(inMsg); + MSG_WriteLong(&outMsg, r); - // checksum feed - r = MSG_ReadLong(inMsg); - MSG_WriteLong(&outMsg, r); + // checksum feed + r = MSG_ReadLong(inMsg); + MSG_WriteLong(&outMsg, r); + } break; case svc_snapshot: { @@ -1108,6 +1172,12 @@ static void CL_WriteNonDeltaDemoMessage (msg_t *inMsg) //CL_ParseSnapshot( msg, NULL, qfalse ); haveSnapshot = qtrue; + + if (clc.realProtocol < 46) { + r = MSG_ReadLong(inMsg); // Client command sequence. + MSG_WriteLong(&outMsg, r); + } + // server time r = MSG_ReadLong(inMsg); MSG_WriteLong(&outMsg, r); @@ -1141,10 +1211,17 @@ static void CL_WriteNonDeltaDemoMessage (msg_t *inMsg) while (1) { int newnum = MSG_ReadBits(inMsg, GENTITYNUM_BITS); + //Com_Printf("write: %d (%d - %d)\n", newnum, inMsg->readcount, inMsg->cursize); + if (newnum == (MAX_GENTITIES - 1)) { break; } + if (inMsg->readcount > inMsg->cursize) { + Com_Printf("^1%s parse packet entities end of message: %d > %d entity: %d\n", __FUNCTION__, inMsg->readcount, inMsg->cursize, newnum); + break; + } + MSG_ReadDeltaEntity(inMsg, &esFrom, &esTo, newnum); } @@ -1274,6 +1351,15 @@ static void CL_WriteNonDeltaDemoMessage (msg_t *inMsg) } } + + if (clc.realProtocol <= 48) { + // _inMsg.GoToNextByte(); + //Com_Printf("nextbyte %d %d\n", msg->readcount, msg->bit); + if ((inMsg->bit & 7) != 0) { + inMsg->readcount++; + inMsg->bit = inMsg->readcount << 3; + } + } } done: @@ -1287,7 +1373,12 @@ static void CL_WriteNonDeltaDemoMessage (msg_t *inMsg) ////////////////////////////////////////// - MSG_WriteByte(&outMsg, svc_EOF); + //FIXME is this right for protocol <= 48 ?, 2024-08-18 no error in quake3 127 + if (clc.realProtocol < 48) { + MSG_WriteByte(&outMsg, svc_bad); + } else { + MSG_WriteByte(&outMsg, svc_EOF); + } len = LittleLong(cl.snap.messageNum); FS_Write(&len, 4, clc.demoWriteFile); @@ -1298,7 +1389,7 @@ static void CL_WriteNonDeltaDemoMessage (msg_t *inMsg) if (di.firstNonDeltaMessageNumWritten == -1) { di.firstNonDeltaMessageNumWritten = cl.snap.messageNum; } - Com_Printf("writing non delta for %d %d\n", cl.snap.messageNum, clc.serverMessageSequence); + //Com_Printf("writing non delta for %d %d\n", cl.snap.messageNum, clc.serverMessageSequence); } // not very strict validation, just checks if it's close enough to something valid @@ -1307,7 +1398,9 @@ static qboolean CL_CheckForValidServerMessage (msg_t *msg) int cmd; int v; - MSG_Bitstream(msg); + if (!di.olderUncompressedDemo) { + MSG_Bitstream(msg); + } // get the reliable sequence acknowledge number v = MSG_ReadLong( msg ); @@ -1325,6 +1418,13 @@ static qboolean CL_CheckForValidServerMessage (msg_t *msg) return qfalse; } + if (clc.realProtocol <= 48) { + if (msg->readcount == msg->cursize) { + Com_Printf("slkdjfsldkfj\n"); + break; + } + } + cmd = MSG_ReadByte(msg); // See if this is an extension command after the EOF, which means we @@ -1379,6 +1479,10 @@ static qboolean CL_CheckForValidServerMessage (msg_t *msg) //FIXME CL_ParseSnapshot(msg, NULL, clc.serverMessageSequence, qfalse); Com_Printf("%s: snapshot\n", __FUNCTION__); + if (clc.realProtocol < 48) { + MSG_ReadLong(msg); // Client command sequence + } + serverTime = MSG_ReadLong(msg); if (serverTime < 0) { Com_Printf("%s: invalid server time %d\n", __FUNCTION__, serverTime); @@ -1486,7 +1590,7 @@ void CL_ReadDemoMessage (qboolean seeking) if (di.streaming) { di.snapsInDemo = 22965; } - + if ( !di.testParse && di.snapCount < maxRewindBackups && ((!di.gotFirstSnap && !(clc.state >= CA_CONNECTED && clc.state < CA_PRIMED)) || @@ -1535,6 +1639,13 @@ void CL_ReadDemoMessage (qboolean seeking) debug = qtrue; } + //FIXME 2024-07-28 this is a mess, should have commented why it was needed, the stream can have data written after this returns and what this would have detected as not enough data for message might have it afterwards, that could mess up the snap/message count, for something like that maybe only read the demo/stream end position once + + // changeset: 9050:074cb4fb2345 + // user: acano + // date: Thu Oct 21 06:22:18 2021 -0400 + // summary: demo streaming look ahead to allow fast forwarding + stream_demo_look_ahead(); if (!di.waitingForStream) { @@ -1651,8 +1762,28 @@ void CL_ReadDemoMessage (qboolean seeking) //Com_Printf("^3server message sequence: %d -> %d\n", prevServerMessageSequence, clc.serverMessageSequence); } + if (!di.checkedForOlderUncompressedDemo) { + int demoPos; + + demoPos = FS_FTell(clc.demoReadFile); + di.checkedForOlderUncompressedDemo = qtrue; + //di.olderUncompressedDemo = qtrue; + //di.olderUncompressedDemoProtocol = 48; + di.olderUncompressedDemo = check_for_older_uncompressed_demo(); + FS_Seek(clc.demoReadFile, demoPos, FS_SEEK_SET); + //Com_Printf("^2 fffffffffffff hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh %d\n", di.olderUncompressedDemoProtocol); + //FIXME hack for com_protocol check in msg.c + if (di.olderUncompressedDemo) { + Cvar_Set("protocol", va("%d", di.olderUncompressedDemoProtocol)); + } + } + // init the message - MSG_Init( &buf, bufData, sizeof( bufData ) ); + if (di.olderUncompressedDemo) { + MSG_InitOOB( &buf, bufData, sizeof( bufData ) ); + } else { + MSG_Init( &buf, bufData, sizeof( bufData ) ); + } // get the length r = FS_Read (&buf.cursize, 4, clc.demoReadFile); @@ -1745,6 +1876,16 @@ void CL_ReadDemoMessage (qboolean seeking) clc.lastPacketTime = cls.realtime; buf.readcount = 0; + +#if 0 + // testing CL_CheckForValidServerMessage + if (CL_CheckForValidServerMessage(&buf)) { + Com_Printf("gooood\n"); + } + buf.readcount = 0; + buf.bit = 0; +#endif + CL_ParseServerMessage( &buf ); if (!di.testParse && clc.demorecording && clc.demoplaying && !seeking) { @@ -1773,9 +1914,20 @@ void CL_ReadDemoMessage (qboolean seeking) const int voipSize = clc.voipOutgoingDataSize; msg_t fakemsg; byte fakedata[MAX_MSGLEN]; - MSG_Init (&fakemsg, fakedata, sizeof (fakedata)); - MSG_Bitstream (&fakemsg); - MSG_WriteLong (&fakemsg, clc.reliableAcknowledge); + + if (di.olderUncompressedDemo) { + MSG_InitOOB (&fakemsg, fakedata, sizeof(fakedata)); + } else { + MSG_Init (&fakemsg, fakedata, sizeof (fakedata)); + MSG_Bitstream (&fakemsg); + } + + + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol < 48) { + // pass + } else { + MSG_WriteLong (&fakemsg, clc.reliableAcknowledge); + } MSG_WriteByte (&fakemsg, svc_voip); //MSG_WriteShort (&fakemsg, clc.clientNum); //FIXME hack in case demo already has voip @@ -1914,7 +2066,9 @@ static void check_events (const entityState_t *es, int serverTime) clientNum = 0; } - if (((di.protocol == PROTOCOL_QL || di.protocol == 73 || di.protocol == 90) && event == EV_OBITUARY) || (di.protocol == PROTOCOL_Q3 && event == EVQ3_OBITUARY)) { + //Com_Printf("di.protocol %d com_protocol->integer %d event %d\n", di.protocol, com_protocol->integer, event); + + if (((di.protocol == PROTOCOL_QL || di.protocol == 73 || di.protocol == 90) && event == EV_OBITUARY) || ((di.protocol <= 71 && di.protocol >= 48) && event == EVQ3_OBITUARY) || (di.protocol < 48 && event == EVQ3DM3_OBITUARY)) { int target, attacker, mod; if (di.obitNum >= MAX_DEMO_OBITS) { @@ -2004,7 +2158,7 @@ static void check_events (const entityState_t *es, int serverTime) // di.clientAlive[target] = qfalse; //} return; - } else if (((com_protocol->integer == PROTOCOL_QL || com_protocol->integer == 73 || com_protocol->integer == 90) && (event == EV_ITEM_PICKUP_SPEC || event == EV_ITEM_PICKUP)) || (com_protocol->integer == PROTOCOL_Q3 && event == EVQ3_ITEM_PICKUP)) { + } else if (((com_protocol->integer == PROTOCOL_QL || com_protocol->integer == 73 || com_protocol->integer == 90) && (event == EV_ITEM_PICKUP_SPEC || event == EV_ITEM_PICKUP)) || ((com_protocol->integer <= 71 && com_protocol->integer >= 48) && event == EVQ3_ITEM_PICKUP) || (com_protocol->integer < 48 && event == EVQ3DM3_ITEM_PICKUP)) { //Com_Printf("%d pickup item %d\n", cl.snap.serverTime, es->eventParm); if (di.numItemPickups >= MAX_ITEM_PICKUPS) { Com_Printf("^3max pickups\n"); @@ -2027,6 +2181,8 @@ static void check_events (const entityState_t *es, int serverTime) item = &bg_itemlist[index]; } else { index = es->eventParm; + + // bg_itemListCpma should be ok if you only check armor and megahealth pickups (ex: quake3 1.25p has different bg_itemlist but it's the same up to those items) if (1) { //(di.cpma) { //FIXME item = &bg_itemlistCpma[index]; } else { @@ -2096,6 +2252,7 @@ static void parse_new_snapshot (const clSnapshot_t *snap, const clSnapshot_t *ol entityState_t es; int i; int count; + int et_events; if (!oldSnap) { Com_Printf("%s() !oldSnap\n", __FUNCTION__); @@ -2186,9 +2343,15 @@ static void parse_new_snapshot (const clSnapshot_t *snap, const clSnapshot_t *ol } } + if (di.protocol < 48) { + et_events = 12; + } else { + et_events = ET_EVENTS; // 13 + } + // CG_CheckEvents //FIXME 2020-06-15 encrypted cpma mvd - if (es.eType > ET_EVENTS) { + if (es.eType > et_events) { if (di.entityPreviousEvent[num]) { goto skip; // already fired } @@ -2197,7 +2360,7 @@ static void parse_new_snapshot (const clSnapshot_t *snap, const clSnapshot_t *ol es.number = es.otherEntityNum; } di.entityPreviousEvent[num] = 1; - es.event = es.eType - ET_EVENTS; + es.event = es.eType - et_events; } else { // check for events riding with another entity if (es.event == di.entityPreviousEvent[num]) { @@ -2218,6 +2381,130 @@ static void parse_new_snapshot (const clSnapshot_t *snap, const clSnapshot_t *ol } } +static qboolean check_for_older_uncompressed_demo (void) +{ + int r; + msg_t buf; + byte bufData[MAX_MSGLEN]; + int s; + int i; + qboolean hasProtocol = qfalse; + qboolean hasGamename = qfalse; + qboolean hasVersion = qfalse; + qboolean hasMapname = qfalse; + + //FIXME demo streaming + + FS_Seek(clc.demoReadFile, 0, FS_SEEK_SET); + + // get the sequence number + r = FS_Read(&s, 4, clc.demoReadFile); + if (r != 4) { + FS_Seek(clc.demoReadFile, di.demoPos, FS_SEEK_SET); + return qfalse; + } + + MSG_InitOOB(&buf, bufData, sizeof(bufData)); + + // get the length + r = FS_Read(&buf.cursize, 4, clc.demoReadFile); + if (r != 4) { + FS_Seek(clc.demoReadFile, di.demoPos, FS_SEEK_SET); + return qfalse; + } + buf.cursize = LittleLong(buf.cursize); + if (buf.cursize == -1) { + // /stoprecord writes last sequence number and length as -1 + FS_Seek(clc.demoReadFile, di.demoPos, FS_SEEK_SET); + return qfalse; + } + + if (buf.cursize > buf.maxsize) { + FS_Seek(clc.demoReadFile, di.demoPos, FS_SEEK_SET); + return qfalse; + } + + if (buf.cursize < 0) { + FS_Seek(clc.demoReadFile, di.demoPos, FS_SEEK_SET); + return qfalse; + } + + r = FS_Read( buf.data, buf.cursize, clc.demoReadFile ); + if ( r != buf.cursize ) { + // maybe truncated demo file + //Com_Printf( "Demo file was truncated.\n"); + FS_Seek(clc.demoReadFile, di.demoPos, FS_SEEK_SET); + return qfalse; + } + + //clc.lastPacketTime = cls.realtime; + buf.readcount = 0; + //CL_ParseServerMessage( &buf ); + + // parse server message + + // get the reliable sequence acknowledge number + // protocol 43 doesn't have this + // clc.reliableAcknowledge = MSG_ReadLong( msg ); + + //cmd = MSG_ReadByte(&buf); + + //Com_Printf(" cmd %d\n", cmd); + + // just look through the data to find required settings: + // protocol, gamename, version, mapname + + for (i = 0; i < buf.cursize; i++) { + int bytesLeft = buf.cursize - i; + const char *str = (const char *) (buf.data + i); + + // protocol/46 + if (!hasProtocol && bytesLeft >= 11) { + if (!Q_strncmp(str, "protocol\\", 9)) { + char b[3]; + + hasProtocol = qtrue; + + b[0] = str[9]; + b[1] = str[10]; + b[2] = '\0'; + + di.olderUncompressedDemoProtocol = atoi(b); + } + } + + // gamename/ + if (!hasGamename && bytesLeft >= 10) { + if (!Q_strncmp(str, "gamename\\", 9)) { + hasGamename = qtrue; + } + } + + // version/ + if (!hasVersion && bytesLeft >= 9) { + if (!Q_strncmp(str, "version\\", 8)) { + hasVersion = qtrue; + } + } + + // mapname/ + if (!hasMapname && bytesLeft >= 9) { + if (!Q_strncmp(str, "mapname\\", 8)) { + hasMapname = qtrue; + } + } + } + + FS_Seek(clc.demoReadFile, di.demoPos, FS_SEEK_SET); + + if (hasProtocol && hasGamename && hasVersion && hasMapname) { + Com_Printf("^5older demo protocol detected: %d\n", di.olderUncompressedDemoProtocol); + return qtrue; + } + + return qfalse; +} + extern qboolean Msg_TestParse; extern qboolean Msg_Abort; @@ -2232,8 +2519,6 @@ static void parse_demo (void) clSnapshot_t *oldSnap = NULL; int i; - //return; - tstart = Sys_Milliseconds(); //memset(&di, 0, sizeof(di)); di.gameStartTime = -1; @@ -2249,6 +2534,24 @@ static void parse_demo (void) Msg_TestParse = qtrue; di.demoPos = FS_FTell(clc.demoReadFile); + + // check for protocol 46 - 48 + //di.olderUncompressedDemo = check_for_older_uncompressed_demo(); + if (!di.checkedForOlderUncompressedDemo) { + int demoPos; + + demoPos = FS_FTell(clc.demoReadFile); + di.checkedForOlderUncompressedDemo = qtrue; + //di.olderUncompressedDemo = qtrue; + //di.olderUncompressedDemoProtocol = 48; + di.olderUncompressedDemo = check_for_older_uncompressed_demo(); + FS_Seek(clc.demoReadFile, demoPos, FS_SEEK_SET); + //FIXME hack for com_protocol check in msg.c + if (di.olderUncompressedDemo) { + Cvar_Set("protocol", va("%d", di.olderUncompressedDemoProtocol)); + } + } + // get gameState CL_ReadDemoMessage(qfalse); //CL_SkipDemoMessage(); @@ -2356,7 +2659,6 @@ static void parse_demo (void) //Com_Printf("...\n"); } - //oldSnap = snap; } @@ -2503,21 +2805,42 @@ static void stream_demo_look_ahead (void) // there is enough demo file data to read message - MSG_Init(&buf, bufData, sizeof(bufData)); - buf.cursize = messageLength; - - if (buf.cursize > buf.maxsize) { + if (messageLength > sizeof(bufData)) { Com_Printf("^1%s() demo message length greater than max size, demo position %d\n", __FUNCTION__, di.demoPos); di.endOfDemo = qtrue; break; } - if (buf.cursize < 0) { // -1 handled above + if (messageLength < 0) { // -1 handled above Com_Printf("^1%s() negative message size, demo position %d\n", __FUNCTION__, di.demoPos); di.endOfDemo = qtrue; break; } + if (!di.checkedForOlderUncompressedDemo) { + int demoPos; + + demoPos = FS_FTell(clc.demoReadFile); + di.checkedForOlderUncompressedDemo = qtrue; + //di.olderUncompressedDemo = qtrue; + //di.olderUncompressedDemoProtocol = 48; + di.olderUncompressedDemo = check_for_older_uncompressed_demo(); + FS_Seek(clc.demoReadFile, demoPos, FS_SEEK_SET); + //FIXME hack for com_protocol check in msg.c + if (di.olderUncompressedDemo) { + Cvar_Set("protocol", va("%d", di.olderUncompressedDemoProtocol)); + } + //Com_Printf("^2hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh %d\n", di.olderUncompressedDemoProtocol); + } + + if (di.olderUncompressedDemo) { + MSG_InitOOB(&buf, bufData, sizeof(bufData)); + } else { + MSG_Init(&buf, bufData, sizeof(bufData)); + } + + buf.cursize = messageLength; + r = FS_Read(buf.data, buf.cursize, clc.demoReadFile); if (r != buf.cursize) { // truncated demo @@ -2576,19 +2899,27 @@ static void CL_ReadExtraDemoMessage (demoFile_t *df) if ( r != 4 ) { //CL_DemoCompleted (); Com_Printf("demoFile %d ended\n", df->f); + //FIXME + //Com_Error(ERR_DROP, "FIXME other demo file truncated"); return; } df->serverMessageSequence = LittleLong( s ); //Com_Printf("demoFile %d sequence %d (%d)\n", df->f, df->serverMessageSequence, clc.serverMessageSequence); // init the message - MSG_Init(&buf, bufData, sizeof(bufData)); + if (clc.realProtocol <= 48) { + MSG_InitOOB(&buf, bufData, sizeof(bufData)); + } else { + MSG_Init(&buf, bufData, sizeof(bufData)); + } // get the length r = FS_Read (&buf.cursize, 4, df->f); if ( r != 4 ) { //CL_DemoCompleted (); Com_Printf("demoFile %d truncated\n", df->f); + //FIXME + //Com_Error(ERR_DROP, "FIXME other demo file truncated"); return; } buf.cursize = LittleLong( buf.cursize ); @@ -2673,6 +3004,34 @@ static qhandle_t CL_OpenDemoFile (const char *arg, qboolean streaming) // check if demo name didn't have an extension if (!file) { for (i = 0; i < ARRAY_LEN(demo_protocols) && !file; i++) { + if (demo_protocols[i] > 43 && demo_protocols[i] < 48) { + // already checked .dm3 with protocol 43 + continue; + } + + if (demo_protocols[i] == 43) { + Com_sprintf(name, sizeof(name), "%s.dm3", demoPathName); + FS_FOpenFileRead(name, &file, qtrue); + if (!file) { + Com_sprintf(name, sizeof(name), "%s.DM3", demoPathName); + FS_FOpenFileRead(name, &file, qtrue); + if (!file) { + // :/ + Com_sprintf(name, sizeof(name), "%s.dM3", demoPathName); + FS_FOpenFileRead(name, &file, qtrue); + if (!file) { + // :{ :/ + Com_sprintf(name, sizeof(name), "%s.Dm3", demoPathName); + FS_FOpenFileRead(name, &file, qtrue); + } + } + } + + if (file) { + break; + } + } + Com_sprintf(name, sizeof(name), "%s.dm_%d", demoPathName, demo_protocols[i]); FS_FOpenFileRead(name, &file, qtrue); if (!file) { @@ -2945,7 +3304,7 @@ void CL_StreamDemo_f (void) //FIXME memset(&di, 0, sizeof(di)); - + //FIXME for (i = 0; i < MAX_DEMO_FILES; i++) { di.demoFiles[i].num = i; @@ -2991,6 +3350,7 @@ void CL_StreamDemo_f (void) Con_Close(); //parse_demo(); + //FIXME without parse_demo() how do you check for protocol <= 48? // CL_CheckWorkshopDownload() advances to CA_CONNECTED clc.state = CA_DOWNLOADINGWORKSHOPS; @@ -3431,7 +3791,7 @@ void CL_ForwardCommandToServer( const char *string ) { cmd = Cmd_Argv(0); //printf("cmd: '%s' '%s'\n", cmd, string); - + // ignore key up commands if ( cmd[0] == '-' ) { return; diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c index b4fb3e6..162a45b 100644 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -56,6 +56,9 @@ static void Parse_Error (int code, const char *fmt, ...) if (di.testParse) { Com_Printf(S_COLOR_RED "demo error: '%s'\n", errorMsg); + if (Cvar_VariableIntegerValue("debug_demo_strict")) { + Com_Error(code, "%s", errorMsg); + } } else { if (com_brokenDemo->integer) { Com_Printf(S_COLOR_RED "demo error: '%s'\n", errorMsg); @@ -133,6 +136,22 @@ void CL_ParsePacketEntities( msg_t *msg, const clSnapshot_t *oldframe, clSnapsho } while ( 1 ) { + +#if 0 + Com_Printf("ppack %d : %d\n", msg->readcount, msg->cursize); + + if (msg->readcount == msg->cursize) { + Com_Printf("xxxxxxxxxuuuuuuuuuuuuuuu here\n"); + } + + //FIXME protocol + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol < 48) { + if (msg->readcount == msg->cursize) { + Com_Printf("uuuuuuuuuuuuuuu here\n"); + } + } +#endif + // read the entity index number newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); @@ -366,7 +385,6 @@ static void CL_ParseExtraSnapshot (demoFile_t *df, msg_t *msg, clSnapshot_t *sn, //cl.newSnapshots = qtrue; - //Com_Printf("%s snap set\n", __FUNCTION__); } @@ -408,6 +426,11 @@ void CL_ParseSnapshot (msg_t *msg, clSnapshot_t *sn, int serverMessageSequence, newSnap.serverCommandNum = clc.serverCommandSequence; //Com_Printf("parse snap: %d\n", newSnap.serverCommandNum); + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol < 46) { + //FIXME store/use + MSG_ReadLong(msg); // Client command sequence. + } + newSnap.serverTime = MSG_ReadLong( msg ); // if we were just unpaused, we can only *now* really let the @@ -773,15 +796,21 @@ static void CL_ParseExtraGamestate (demoFile_t *df, msg_t *msg) // parse all the configstrings and baselines //cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings - //Com_Printf("parse gamestate...\n"); + //Com_Printf("parse extra gamestate... buf->oob %d\n", msg->oob); while ( 1 ) { cmd = MSG_ReadByte( msg ); + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48 && cmd == svc_bad) { + break; + } + if ( cmd == svc_EOF ) { break; } + //Com_Printf("extra cmd %d\n", cmd); + if ( cmd == svc_configstring ) { //int len; @@ -805,12 +834,14 @@ static void CL_ParseExtraGamestate (demoFile_t *df, msg_t *msg) #endif //Com_Printf("cs %d '%s'\n", i, s); + //Com_Printf("cs %d\n", i); // append it to the gameState string buffer //cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount; //Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 ); //cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { + //Com_Printf("extra delta entity\n"); newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { //Parse_Error( ERR_DROP, "Baseline number out of range: %i", newnum ); @@ -829,14 +860,19 @@ static void CL_ParseExtraGamestate (demoFile_t *df, msg_t *msg) } } - //clc.clientNum = MSG_ReadLong(msg); - //clientNum = MSG_ReadLong(msg); - MSG_ReadLong(msg); + //FIXME protocol >= 66 + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48) { + // pass, no clientNum or checksumFeed + } else { + //clc.clientNum = MSG_ReadLong(msg); + //clientNum = MSG_ReadLong(msg); + MSG_ReadLong(msg); - // read the checksum feed - //clc.checksumFeed = MSG_ReadLong( msg ); - //checksumFeed = MSG_ReadLong(msg); - MSG_ReadLong(msg); + // read the checksum feed + //clc.checksumFeed = MSG_ReadLong( msg ); + //checksumFeed = MSG_ReadLong(msg); + MSG_ReadLong(msg); + } // parse useful values out of CS_SERVERINFO //CL_ParseServerInfo(); @@ -877,6 +913,12 @@ void CL_ParseGamestate( msg_t *msg ) { while ( 1 ) { cmd = MSG_ReadByte( msg ); + //Com_Printf("ggggg %d (%d %d)\n", cmd, msg->readcount, msg->bit); + + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48 && cmd == svc_bad) { + break; + } + if ( cmd == svc_EOF ) { break; } @@ -902,6 +944,8 @@ void CL_ParseGamestate( msg_t *msg ) { return; } + //Com_Printf("config string %d: '%s'\n", i, s); + // server info, get protocol here if (i == 0) { const char *value; @@ -914,7 +958,9 @@ void CL_ParseGamestate( msg_t *msg ) { Cvar_Set("real_protocol", value); clc.realProtocol = p; - if (p >= 66 && p <= 71) { + if (p >= 43 && p <= 48) { + Cvar_Set("protocol", va("%d", p)); + } else if (p >= 66 && p <= 71) { Cvar_Set("protocol", va("%d", PROTOCOL_Q3)); } else if (p == 73) { //FIXME define Cvar_Set("protocol", "73"); @@ -934,12 +980,24 @@ void CL_ParseGamestate( msg_t *msg ) { Com_Printf("^5protocol not set, setting based on file extension and then checking com_protocol\n"); //ext = COM_GetExtension(DemoNames[0]); ext = COM_GetExtension(cl_demoFile->string); - if (!Q_stricmpn(ext, "dm_66", 5) || - !Q_stricmpn(ext, "dm_67", 5) || - !Q_stricmpn(ext, "dm_68", 5) || - !Q_stricmpn(ext, "dm_69", 5) || - !Q_stricmpn(ext, "dm_70", 5) || - !Q_stricmpn(ext, "dm_71", 5)) { + + if (!Q_stricmpn(ext, "dm3", 3)) { + Com_Printf("Q3Demo demo extension found\n"); + //FIXME define + //FIXME 2024-09-04 wont really work for 44 - 47 + Cvar_Set("protocol", va("%d", 43)); + } else if (!Q_stricmpn(ext, "dm_46", 5) || + !Q_stricmpn(ext, "dm_47", 5) || + !Q_stricmpn(ext, "dm_48", 5)) { + Com_Printf("Q3 demo extension found\n"); + //FIXME define + Cvar_Set("protocol", va("%d", 48)); + } else if (!Q_stricmpn(ext, "dm_66", 5) || + !Q_stricmpn(ext, "dm_67", 5) || + !Q_stricmpn(ext, "dm_68", 5) || + !Q_stricmpn(ext, "dm_69", 5) || + !Q_stricmpn(ext, "dm_70", 5) || + !Q_stricmpn(ext, "dm_71", 5)) { Com_Printf("Q3 demo extension found\n"); Cvar_Set("protocol", va("%d", PROTOCOL_Q3)); } else { @@ -993,6 +1051,9 @@ void CL_ParseGamestate( msg_t *msg ) { cl.gameState.dataCount += len + 1; } else if ( cmd == svc_baseline ) { newnum = MSG_ReadBits( msg, GENTITYNUM_BITS ); + //newnum = MSG_ReadBits(msg, 9); + + //Com_Printf(" baseline %d\n", newnum); if ( newnum < 0 || newnum >= MAX_GENTITIES ) { Parse_Error( ERR_DROP, "CL_ParseGamesate Baseline number out of range: %i", newnum ); return; @@ -1001,14 +1062,19 @@ void CL_ParseGamestate( msg_t *msg ) { es = &cl.entityBaselines[ newnum ]; MSG_ReadDeltaEntity( msg, &nullstate, es, newnum ); } else { - Parse_Error(ERR_DROP, "CL_ParseGamestate: bad command byte", cmd); + Parse_Error(ERR_DROP, "CL_ParseGamestate: bad command byte %d", cmd); return; } } - clc.clientNum = MSG_ReadLong(msg); - // read the checksum feed - clc.checksumFeed = MSG_ReadLong( msg ); + //FIXME protocol >= 66 + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48) { + // pass, no clientNum or checksumFeed + } else { + clc.clientNum = MSG_ReadLong(msg); + // read the checksum feed + clc.checksumFeed = MSG_ReadLong( msg ); + } // save old gamedir Cvar_VariableStringBuffer("fs_game", oldGame, sizeof(oldGame)); @@ -1665,14 +1731,14 @@ void CL_ParseCommandString( msg_t *msg ) { } //Com_Printf("cs (%d) '%s'\n", seq, s); - + // see if we have already executed stored it off if ( clc.serverCommandSequence >= seq ) { return; } //Com_Printf("^2cs (%d) '%s'\n", seq, s); - + clc.serverCommandSequence = seq; index = seq & (MAX_RELIABLE_COMMANDS-1); @@ -1706,7 +1772,7 @@ void CL_ParseCommandString( msg_t *msg ) { // models used in demos also team switches if ( ((di.protocol == PROTOCOL_QL || di.protocol == 73 || di.protocol == 90) && (csnum >= CS_PLAYERS && csnum < (CS_PLAYERS + MAX_CLIENTS))) || - (di.protocol == PROTOCOL_Q3 && (csnum >= CSQ3_PLAYERS && csnum < (CSQ3_PLAYERS + MAX_CLIENTS))) + (di.protocol <= PROTOCOL_Q3 && (csnum >= CSQ3_PLAYERS && csnum < (CSQ3_PLAYERS + MAX_CLIENTS))) ) { char *model; //char *skin; @@ -1717,8 +1783,8 @@ void CL_ParseCommandString( msg_t *msg ) { //Com_Printf("^3cs (%d): '%s'\n", seq, s); if (di.protocol == PROTOCOL_QL || di.protocol == 73 || di.protocol == 90) { clientNum = csnum - CS_PLAYERS; - } else if (di.protocol == PROTOCOL_Q3) { - clientNum= csnum - CSQ3_PLAYERS; + } else if (di.protocol <= PROTOCOL_Q3) { + clientNum = csnum - CSQ3_PLAYERS; } p = s + strlen("cs XXX "); @@ -2030,10 +2096,17 @@ void CL_ParseServerMessage( msg_t *msg ) { Com_Printf ("------------------\n"); } - MSG_Bitstream(msg); + if (!di.olderUncompressedDemo) { + MSG_Bitstream(msg); + } // get the reliable sequence acknowledge number - clc.reliableAcknowledge = MSG_ReadLong( msg ); + // protocol 43 doesn't have this + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol < 46) { + // pass + } else { + clc.reliableAcknowledge = MSG_ReadLong( msg ); + } // if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) { //Com_Printf("^1***************** skipping reliable sequence: %d %d\n", clc.reliableAcknowledge, clc.reliableSequence); @@ -2051,8 +2124,35 @@ void CL_ParseServerMessage( msg_t *msg ) { return; } + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48) { + if (msg->readcount == msg->cursize) { + SHOWNET( msg, "END OF MESSAGE" ); + //SHOWNET( msg, "END OF MESSAGE (size)" ); + break; + } + } + cmd = MSG_ReadByte( msg ); +#if 0 // testing voip with protocol 43 + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol < 48) { + if (cmd == svc_bad) { + //MSG_ReadByte(msg); + //MSG_ReadByte(msg); + //break; + Com_Printf("%d -> %d svc_bad look ahead: %d\n", msg->readcount, msg->cursize, MSG_LookaheadByte(msg)); + while (msg->readcount <= msg->cursize) { + Com_Printf("%d ", MSG_ReadByte(msg)); + } + + Com_Printf("-==0==-\n"); + break; + } + } +#endif + + //Com_Printf("^3cmd: %d readcount: %d msg->cursize: %d\n", cmd, msg->readcount, msg->cursize); + // See if this is an extension command after the EOF, which means we // have speex voip data. if ((cmd == svc_EOF) && (MSG_LookaheadByte( msg ) == svc_extension)) { @@ -2067,6 +2167,7 @@ void CL_ParseServerMessage( msg_t *msg ) { if (cmd == svc_EOF) { SHOWNET( msg, "END OF MESSAGE" ); + //SHOWNET( msg, "END OF MESSAGE (eof)" ); break; } @@ -2119,6 +2220,15 @@ void CL_ParseServerMessage( msg_t *msg ) { break; #endif } + + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48) { + // _inMsg.GoToNextByte(); + //Com_Printf("nextbyte %d %d\n", msg->readcount, msg->bit); + if ((msg->bit & 7) != 0) { + msg->readcount++; + msg->bit = msg->readcount << 3; + } + } } } @@ -2134,11 +2244,23 @@ void CL_ParseExtraServerMessage (demoFile_t *df, msg_t *msg, qboolean justPeek) Com_Printf ("------------------\n"); } - MSG_Bitstream(msg); + //FIXME protocol 48 check not good + //if (clc.realProtocol > 48) { //(!di.olderUncompressedDemo) { + if (!di.olderUncompressedDemo) { + MSG_Bitstream(msg); + } else { + // pass + //Com_Printf("yes %d %d\n", di.olderUncompressedDemo, di.olderUncompressedDemoProtocol); + } // get the reliable sequence acknowledge number - //reliableAcknowledge = MSG_ReadLong( msg ); - MSG_ReadLong(msg); + // protocol 43 doesn't have this + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol < 48) { + // pass + } else { + //reliableAcknowledge = MSG_ReadLong( msg ); + MSG_ReadLong(msg); + } #if 0 // @@ -2153,10 +2275,17 @@ void CL_ParseExtraServerMessage (demoFile_t *df, msg_t *msg, qboolean justPeek) while ( 1 ) { if ( msg->readcount > msg->cursize ) { //Parse_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message"); - Com_Printf("^1CL_ParseExtraServerMessage() read past end of server message demoFile %d", df->f); + Com_Printf("^1CL_ParseExtraServerMessage() read past end of server message demoFile %d (%d > %d)\n", df->f, msg->readcount, msg->cursize); return; } + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48) { + if (msg->readcount == msg->cursize) { + SHOWNET( msg, "END OF MESSAGE" ); + break; + } + } + cmd = MSG_ReadByte( msg ); // See if this is an extension command after the EOF, which means we @@ -2185,11 +2314,13 @@ void CL_ParseExtraServerMessage (demoFile_t *df, msg_t *msg, qboolean justPeek) } } + //Com_Printf("extra server message %d\n", cmd); + // other commands switch ( cmd ) { default: //Parse_Error (ERR_DROP, "CL_ParseServerMessage: Illegible server message %d", cmd); - Com_Printf("^1CL_ParseExtraServerMessage: Illegible server message %d for demoFile %d", cmd, df->f); + Com_Printf("^1CL_ParseExtraServerMessage: Illegible server message %d for demoFile %d\n", cmd, df->f); return; case svc_nop: break; @@ -2212,5 +2343,14 @@ void CL_ParseExtraServerMessage (demoFile_t *df, msg_t *msg, qboolean justPeek) #endif break; } + + if (di.olderUncompressedDemo && di.olderUncompressedDemoProtocol <= 48) { + // _inMsg.GoToNextByte(); + //Com_Printf("nextbyte %d %d\n", msg->readcount, msg->bit); + if ((msg->bit & 7) != 0) { + msg->readcount++; + msg->bit = msg->readcount << 3; + } + } } } diff --git a/code/client/client.h b/code/client/client.h index 6ea3cae..6922c7b 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -430,6 +430,11 @@ typedef struct { qboolean endOfDemo; qboolean testParse; + // is it protocol 43 - 48 + qboolean checkedForOlderUncompressedDemo; + qboolean olderUncompressedDemo; + int olderUncompressedDemoProtocol; + demoObit_t obit[MAX_DEMO_OBITS]; int obitNum; //qboolean clientAlive[MAX_CLIENTS]; diff --git a/code/game/bg_misc.c b/code/game/bg_misc.c index 16f9fad..c677881 100644 --- a/code/game/bg_misc.c +++ b/code/game/bg_misc.c @@ -3890,6 +3890,933 @@ Only in One Flag CTF games {NULL} }; +const gitem_t bg_itemlistQ3_125p[] = +{ + { + NULL, + NULL, + { NULL, + NULL, + NULL, NULL} , +/* icon */ NULL, +/* pickup */ NULL, + 0, + 0, + 0, +/* precache */ "", +/* sounds */ "" + }, // leave index 0 alone + + // + // ARMOR + // + +/*QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_armor_shard", + "sound/misc/ar1_pkup.wav", + { "models/powerups/armor/shard.md3", + "models/powerups/armor/shard_sphere.md3", + NULL, NULL} , +/* icon */ "icons/iconr_shard", +/* pickup */ "Armor Shard", + 5, + IT_ARMOR, + 0, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_armor_combat", + "sound/misc/ar2_pkup.wav", + { "models/powerups/armor/armor_yel.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconr_yellow", +/* pickup */ "Yellow Armor", + 50, + IT_ARMOR, + 0, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_armor_body", + "sound/misc/ar2_pkup.wav", + { "models/powerups/armor/armor_red.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconr_red", +/* pickup */ "Red Armor", // was "Heavy Armor" in q3 + 100, + IT_ARMOR, + 0, +/* precache */ "", +/* sounds */ "" + }, + + // + // health + // +/*QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_health_small", + "sound/items/s_health.wav", + { "models/powerups/health/small_cross.md3", + "models/powerups/health/small_sphere.md3", + NULL, NULL }, +/* icon */ "icons/iconh_green", +/* pickup */ "5 Health", + 5, + IT_HEALTH, + 0, +/* precache */ "", +/* sounds */ "" + }, + + // 5 +/*QUAKED item_health (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_health", + "sound/items/n_health.wav", + { "models/powerups/health/medium_cross.md3", + "models/powerups/health/medium_sphere.md3", + NULL, NULL }, +/* icon */ "icons/iconh_yellow", +/* pickup */ "25 Health", + 25, + IT_HEALTH, + 0, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_health_large", + "sound/items/l_health.wav", + { "models/powerups/health/large_cross.md3", + "models/powerups/health/large_sphere.md3", + NULL, NULL }, +/* icon */ "icons/iconh_red", +/* pickup */ "50 Health", + 50, + IT_HEALTH, + 0, +/* precache */ "", +/* sounds */ "" + }, + + // 7 +/*QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_health_mega", + "sound/items/m_health.wav", + { "models/powerups/health/mega_cross.md3", + "models/powerups/health/mega_sphere.md3", + NULL, NULL }, +/* icon */ "icons/iconh_mega", +/* pickup */ "Mega Health", + 100, + IT_HEALTH, + 0, +/* precache */ "", +/* sounds */ "" + }, + + + // + // WEAPONS + // + + // 8, yes +/*QUAKED weapon_gauntlet (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_gauntlet", + "sound/misc/w_pkup.wav", + { "models/weapons2/gauntlet/gauntlet.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_gauntlet", +/* pickup */ "Gauntlet", + 0, + IT_WEAPON, + WP_GAUNTLET, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_shotgun", + "sound/misc/w_pkup.wav", + { "models/weapons2/shotgun/shotgun.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_shotgun", +/* pickup */ "Shotgun", + 10, + IT_WEAPON, + WP_SHOTGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_machinegun", + "sound/misc/w_pkup.wav", + { "models/weapons2/machinegun/machinegun.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_machinegun", +/* pickup */ "Machinegun", + 40, + IT_WEAPON, + WP_MACHINEGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_grenadelauncher", + "sound/misc/w_pkup.wav", + { "models/weapons2/grenadel/grenadel.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_grenade", +/* pickup */ "Grenade Launcher", + 10, + IT_WEAPON, + WP_GRENADE_LAUNCHER, +/* precache */ "", +/* sounds */ "sound/weapons/grenade/hgrenb1a.wav sound/weapons/grenade/hgrenb2a.wav" + }, + + // 12 +/*QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_rocketlauncher", + "sound/misc/w_pkup.wav", + { "models/weapons2/rocketl/rocketl.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_rocket", +/* pickup */ "Rocket Launcher", + 10, + IT_WEAPON, + WP_ROCKET_LAUNCHER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_lightning", + "sound/misc/w_pkup.wav", + { "models/weapons2/lightning/lightning.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_lightning", +/* pickup */ "Lightning Gun", + 100, + IT_WEAPON, + WP_LIGHTNING, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_railgun", + "sound/misc/w_pkup.wav", + { "models/weapons2/railgun/railgun.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_railgun", +/* pickup */ "Railgun", + 10, + IT_WEAPON, + WP_RAILGUN, +/* precache */ "", +/* sounds */ "" + }, + + // 15 +/*QUAKED weapon_plasmagun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_plasmagun", + "sound/misc/w_pkup.wav", + { "models/weapons2/plasma/plasma.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_plasma", +/* pickup */ "Plasma Gun", + 50, + IT_WEAPON, + WP_PLASMAGUN, +/* precache */ "", +/* sounds */ "" + }, + + // 16, yes +/*QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_bfg", + "sound/misc/w_pkup.wav", + { "models/weapons2/bfg/bfg.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_bfg", +/* pickup */ "BFG10K", + 20, + IT_WEAPON, + WP_BFG, +/* precache */ "", +/* sounds */ "" + }, + + // 17, yes +/*QUAKED weapon_grapplinghook (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_grapplinghook", + "sound/misc/w_pkup.wav", + { "models/weapons2/grapple/grapple.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_grapple", +/* pickup */ "Grappling Hook", + 0, + IT_WEAPON, + WP_GRAPPLING_HOOK, +/* precache */ "", +/* sounds */ "" + }, + + /*QUAKED weapon_nailgun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_nailgun", + "sound/misc/w_pkup.wav", + { "models/weapons/nailgun/nailgun.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_nailgun", +/* pickup */ "Nailgun", + 10, + IT_WEAPON, + WP_NAILGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED weapon_prox_launcher (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_prox_launcher", + "sound/misc/w_pkup.wav", + { "models/weapons/proxmine/proxmine.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_proxlauncher", +/* pickup */ "Prox Launcher", + 5, + IT_WEAPON, + WP_PROX_LAUNCHER, +/* precache */ "", +/* sounds */ "sound/weapons/proxmine/wstbtick.wav " + "sound/weapons/proxmine/wstbactv.wav " + "sound/weapons/proxmine/wstbimpl.wav " + "sound/weapons/proxmine/wstbimpm.wav " + "sound/weapons/proxmine/wstbimpd.wav " + "sound/weapons/proxmine/wstbactv.wav" + }, + + // 20, yes +/*QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "weapon_chaingun", + "sound/misc/w_pkup.wav", + { "models/weapons/vulcan/vulcan.md3", + NULL, NULL, NULL}, +/* icon */ "icons/iconw_chaingun", +/* pickup */ "Chaingun", + 80, + IT_WEAPON, + WP_CHAINGUN, +/* precache */ "", +/* sounds */ "sound/weapons/vulcan/wvulwind.wav" + }, + + // hereeeeeeeee + // + // AMMO ITEMS + // + +/*QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_shells", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/shotgunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_shotgun", +/* pickup */ "Shells", + 10, + IT_AMMO, + WP_SHOTGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_bullets", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/machinegunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_machinegun", +/* pickup */ "Bullets", + 50, + IT_AMMO, + WP_MACHINEGUN, +/* precache */ "", +/* sounds */ "" + }, + + // 23, yes +/*QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_grenades", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/grenadeam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_grenade", +/* pickup */ "Grenades", + 5, + IT_AMMO, + WP_GRENADE_LAUNCHER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_cells", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/plasmaam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_plasma", +/* pickup */ "Cells", + 30, + IT_AMMO, + WP_PLASMAGUN, +/* precache */ "", +/* sounds */ "" + }, + + // 25, yes +/*QUAKED ammo_lightning (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_lightning", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/lightningam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_lightning", +/* pickup */ "Lightning", + 60, + IT_AMMO, + WP_LIGHTNING, +/* precache */ "", +/* sounds */ "" + }, + + // 26, yes +/*QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_rockets", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/rocketam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_rocket", +/* pickup */ "Rockets", + 5, + IT_AMMO, + WP_ROCKET_LAUNCHER, +/* precache */ "", +/* sounds */ "" + }, + + // 27, yes +/*QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_slugs", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/railgunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_railgun", +/* pickup */ "Slugs", + 10, + IT_AMMO, + WP_RAILGUN, +/* precache */ "", +/* sounds */ "" + }, + + // 28, yes +/*QUAKED ammo_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_bfg", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/bfgam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_bfg", +/* pickup */ "Bfg Ammo", + 15, + IT_AMMO, + WP_BFG, +/* precache */ "", +/* sounds */ "" + }, + + // 29, yes + + /*QUAKED ammo_nails (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_nails", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/nailgunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_nailgun", +/* pickup */ "Nails", + 20, + IT_AMMO, + WP_NAILGUN, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_mines (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_mines", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/proxmineam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_proxlauncher", +/* pickup */ "Proximity Mines", + 10, + IT_AMMO, + WP_PROX_LAUNCHER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED ammo_belt (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "ammo_belt", + "sound/misc/am_pkup.wav", + { "models/powerups/ammo/chaingunam.md3", + NULL, NULL, NULL}, +/* icon */ "icons/icona_chaingun", +/* pickup */ "Chaingun Belt", + 100, + IT_AMMO, + WP_CHAINGUN, +/* precache */ "", +/* sounds */ "" + }, + + // + // HOLDABLE ITEMS + // + + // 32, yes +/*QUAKED holdable_teleporter (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_teleporter", + "sound/items/holdable.wav", + { "models/powerups/holdable/teleporter.md3", + NULL, NULL, NULL}, +/* icon */ "icons/teleporter", +/* pickup */ "Personal Teleporter", + 60, + IT_HOLDABLE, + HI_TELEPORTER, +/* precache */ "", +/* sounds */ "" + }, + + // 33, yes +/*QUAKED holdable_medkit (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_medkit", + "sound/items/holdable.wav", + { + "models/powerups/holdable/medkit.md3", + "models/powerups/holdable/medkit_sphere.md3", + NULL, NULL}, +/* icon */ "icons/medkit", +/* pickup */ "Medkit", + 60, + IT_HOLDABLE, + HI_MEDKIT, +/* precache */ "", +/* sounds */ "sound/items/use_medkit.wav" + }, + + + // 34, yes + /*QUAKED holdable_kamikaze (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_kamikaze", + "sound/items/holdable.wav", + { "models/powerups/kamikazi.md3", + NULL, NULL, NULL}, +/* icon */ "icons/kamikaze", +/* pickup */ "Kamikaze", + 60, + IT_HOLDABLE, + HI_KAMIKAZE, +/* precache */ "", +/* sounds */ "sound/items/kamikazerespawn.wav" + }, + + // 35, yes +/*QUAKED holdable_portal (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_portal", + "sound/items/holdable.wav", + { "models/powerups/holdable/porter.md3", + NULL, NULL, NULL}, +/* icon */ "icons/portal", +/* pickup */ "Portal", + 60, + IT_HOLDABLE, + HI_PORTAL, +/* precache */ "", +/* sounds */ "" + }, + + // 36, yes +/*QUAKED holdable_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "holdable_invulnerability", + "sound/items/holdable.wav", + { "models/powerups/holdable/invulnerability.md3", + NULL, NULL, NULL}, +/* icon */ "icons/invulnerability", +/* pickup */ "Invulnerability", + 60, + IT_HOLDABLE, + HI_INVULNERABILITY, +/* precache */ "", +/* sounds */ "" + }, + + // + // POWERUP ITEMS + // + + // 37, yes +/*QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_quad", + //"sound/items/quaddamage.wav", + "sound/vo/quad_damage.wav", + { "models/powerups/instant/quad.md3", + "models/powerups/instant/quad_ring.md3", + NULL, NULL }, +/* icon */ "icons/quad", +/* pickup */ "Quad Damage", + 30, + IT_POWERUP, + PWOLD_QUAD, +/* precache */ "", +/* sounds */ "sound/items/damage3.wav" + }, + + // 38, yes +/*QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_enviro", + "sound/vo/battlesuit.ogg", + { "models/powerups/instant/enviro.md3", + "models/powerups/instant/enviro_ring.md3", + NULL, NULL }, +/* icon */ "icons/envirosuit", +/* pickup */ "Battle Suit", + 30, + IT_POWERUP, + PWOLD_BATTLESUIT, +/* precache */ "", +/* sounds */ "sound/items/protect3.wav" + }, + + // 39, yes +/*QUAKED item_haste (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_haste", + "sound/vo/haste.wav", + { "models/powerups/instant/haste.md3", + "models/powerups/instant/haste_ring.md3", + NULL, NULL }, +/* icon */ "icons/haste", +/* pickup */ "Haste", // was "Speed" in q3 + 30, + IT_POWERUP, + PWOLD_HASTE, +/* precache */ "", +/* sounds */ "" + }, + + // 40, yes +/*QUAKED item_invis (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_invis", + "sound/vo/invisibility.wav", + { "models/powerups/instant/invis.md3", + "models/powerups/instant/invis_ring.md3", + NULL, NULL }, +/* icon */ "icons/invis", +/* pickup */ "Invisibility", + 30, + IT_POWERUP, + PWOLD_INVIS, +/* precache */ "", +/* sounds */ "" + }, + + // 41, yes +/*QUAKED item_regen (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_regen", + "sound/vo/regeneration.wav", + { "models/powerups/instant/regen.md3", + "models/powerups/instant/regen_ring.md3", + NULL, NULL }, +/* icon */ "icons/regen", +/* pickup */ "Regeneration", + 30, + IT_POWERUP, + PWOLD_REGEN, +/* precache */ "", +/* sounds */ "sound/items/regen.wav" + }, + + // 42, yes +/*QUAKED item_flight (.3 .3 1) (-16 -16 -16) (16 16 16) suspended +*/ + { + "item_flight", + "sound/items/flight.wav", + { "models/powerups/instant/flight.md3", + "models/powerups/instant/flight_ring.md3", + NULL, NULL }, +/* icon */ "icons/flight", +/* pickup */ "Flight", + 60, + IT_POWERUP, + PWOLD_FLIGHT, +/* precache */ "", +/* sounds */ "sound/items/flight.wav" + }, + + // item_scout + // item_guard + // item_doubler + // item_ammoregen + + // + // PERSISTANT POWERUP ITEMS + // + + // 43, yes +/*QUAKED item_scout (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam +*/ + { + "item_scout", + "sound/items/scout.wav", + { "models/powerups/scout.md3", + NULL, NULL, NULL }, +/* icon */ "icons/scout", +/* pickup */ "Scout", + 30, + IT_PERSISTANT_POWERUP, + PWOLD_SCOUT, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_guard (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam +*/ + { + "item_guard", + "sound/items/guard.wav", + { "models/powerups/guard.md3", + NULL, NULL, NULL }, +/* icon */ "icons/guard", +/* pickup */ "Guard", + 30, + IT_PERSISTANT_POWERUP, + PWOLD_GUARD, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_doubler (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam +*/ + { + "item_doubler", + "sound/items/doubler.wav", + { "models/powerups/doubler.md3", + NULL, NULL, NULL }, +/* icon */ "icons/doubler", +/* pickup */ "Damage", // 2019-02-05 was "Doubler" called "Damage" in quakelive + 30, + IT_PERSISTANT_POWERUP, + PWOLD_DOUBLER, +/* precache */ "", +/* sounds */ "" + }, + +/*QUAKED item_doubler (.3 .3 1) (-16 -16 -16) (16 16 16) suspended redTeam blueTeam +*/ + { + "item_armorregen", + "sound/items/armorregen.wav", + { "models/powerups/ammo.md3", + NULL, NULL, NULL }, +/* icon */ "icons/armor_regen", +/* pickup */ "Armor Regen", // changed from "Ammo Regen" in q3 + 30, + IT_PERSISTANT_POWERUP, + PWOLD_ARMORREGEN, +/* precache */ "", +/* sounds */ "" + }, + + // 47, yes +/*QUAKED team_CTF_redflag (1 0 0) (-16 -16 -16) (16 16 16) +Only in CTF games +*/ + { + "team_CTF_redflag", + NULL, + { "models/flags/r_flag.md3", + NULL, NULL, NULL }, +/* icon */ "gfx/2d/flag_status/red_flag_at_base.png", +/* pickup */ "Red Flag", + 0, + IT_TEAM, + PWOLD_REDFLAG, +/* precache */ "", +/* sounds */ "" + }, + + // 48, yes +/*QUAKED team_CTF_blueflag (0 0 1) (-16 -16 -16) (16 16 16) +Only in CTF games +*/ + { + "team_CTF_blueflag", + NULL, + { "models/flags/b_flag.md3", + NULL, NULL, NULL }, +/* icon */ "gfx/2d/flag_status/blue_flag_at_base.png", +/* pickup */ "Blue Flag", + 0, + IT_TEAM, + PWOLD_BLUEFLAG, +/* precache */ "", +/* sounds */ "" + }, + + // maybe accidentally enabled? + //#ifdef MISSIONPACK +#if 1 + + // 49, yes + /*QUAKED team_CTF_neutralflag (0 0 1) (-16 -16 -16) (16 16 16) +Only in One Flag CTF games +*/ + { + "team_CTF_neutralflag", + NULL, + { "models/flags/n_flag.md3", + NULL, NULL, NULL }, +/* icon */ "gfx/2d/flag_status/flag_at_base.png", +/* pickup */ "Neutral Flag", + 0, + IT_TEAM, + PWOLD_NEUTRALFLAG, +/* precache */ "", +/* sounds */ "" + }, + + // 50, yes + { + "item_redcube", + "sound/misc/am_pkup.wav", + { "models/powerups/orb/r_orb.md3", + NULL, NULL, NULL }, +/* icon */ "icons/iconh_rorb", +/* pickup */ "Red Cube", + 0, + IT_TEAM, + 0, +/* precache */ "", +/* sounds */ "" + }, + + // 51, yes + { + "item_bluecube", + "sound/misc/am_pkup.wav", + { "models/powerups/orb/b_orb.md3", + NULL, NULL, NULL }, +/* icon */ "icons/iconh_borb", +/* pickup */ "Blue Cube", + 0, + IT_TEAM, + 0, +/* precache */ "", +/* sounds */ "" + }, +#endif + + // end of list marker + {NULL} +}; + const gitem_t bg_itemlistCpma[] = { { @@ -4816,6 +5743,7 @@ Only in One Flag CTF games //gitem_t *bg_itemList[] = bg_itemlistQ3; int bg_numItems = ARRAY_LEN(bg_itemlist) - 1; int bg_numItemsQ3 = ARRAY_LEN(bg_itemlistQ3) - 1; +int bg_numItemsQ3_125p = ARRAY_LEN(bg_itemlistQ3_125p) - 1; int bg_numItemsCpma = ARRAY_LEN(bg_itemlistCpma) - 1; int bg_numItemsQldm73 = ARRAY_LEN(bg_itemlistQldm73) - 1; int bg_numItemsQldm91 = ARRAY_LEN(bg_itemlistQldm91) - 1; diff --git a/code/game/bg_public.h b/code/game/bg_public.h index f45f47e..fc23ed1 100644 --- a/code/game/bg_public.h +++ b/code/game/bg_public.h @@ -354,7 +354,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define CSQ3_SOUNDS (CSQ3_MODELS+MAX_MODELS) #define CSQ3_PLAYERS (CSQ3_SOUNDS+MAX_SOUNDS) #define CSQ3_LOCATIONS (CSQ3_PLAYERS+MAX_CLIENTS) -#define CSQ3_PARTICLES (CSQ3_LOCATIONS+MAX_LOCATIONS) +#define CSQ3_PARTICLES (CSQ3_LOCATIONS+MAX_LOCATIONS) #define CSQ3_MAX (CSQ3_PARTICLES+MAX_LOCATIONS) @@ -362,6 +362,11 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #error overflow: (CSQ3_MAX) > MAX_CONFIGSTRINGS #endif +// from Uber Demo Tools: dm3 had MAX_CLIENTS set as 128! +#define CSQ3DM3_LOCATIONS (CSQ3_LOCATIONS + 64) +#define CSQ3DM3_PARTICLES (CSQ3DM3_LOCATIONS+MAX_LOCATIONS) +#define CSQ3DM3_MAX (CSQ3DM3_PARTICLES+MAX_LOCATIONS) + // cpma #define CSCPMA_GAMESTATE 672 @@ -1024,6 +1029,122 @@ typedef enum { } entity_event_q3_t; +typedef enum { + EVQ3DM3_NONE, + + EVQ3DM3_FOOTSTEP, + EVQ3DM3_FOOTSTEP_METAL, + EVQ3DM3_FOOTSPLASH, + EVQ3DM3_FOOTWADE, + EVQ3DM3_SWIM, + + EVQ3DM3_STEP_4, // 6 + EVQ3DM3_STEP_8, + EVQ3DM3_STEP_12, + EVQ3DM3_STEP_16, + + EVQ3DM3_FALL_SHORT, + EVQ3DM3_FALL_MEDIUM, + EVQ3DM3_FALL_FAR, + + EVQ3DM3_JUMP_PAD, // boing sound at origin, jump sound on player + + EVQ3DM3_JUMP, // 14 + EVQ3DM3_WATER_TOUCH, // foot touches + EVQ3DM3_WATER_LEAVE, // foot leaves + EVQ3DM3_WATER_UNDER, // head touches + EVQ3DM3_WATER_CLEAR, // head leaves + + EVQ3DM3_ITEM_PICKUP, // normal item pickups are predictable + EVQ3DM3_GLOBAL_ITEM_PICKUP, // powerup / team sounds are broadcast to everyone + + EVQ3DM3_NOAMMO, + EVQ3DM3_CHANGE_WEAPON, + EVQ3DM3_FIRE_WEAPON, // 23 + + // above same as q3 + + EVQ3DM3_USE_ITEM0, // 24 + EVQ3DM3_USE_ITEM1, + EVQ3DM3_USE_ITEM2, + EVQ3DM3_USE_ITEM3, + EVQ3DM3_USE_ITEM4, + EVQ3DM3_USE_ITEM5, + EVQ3DM3_USE_ITEM6, + EVQ3DM3_USE_ITEM7, + EVQ3DM3_USE_ITEM8, + EVQ3DM3_USE_ITEM9, + EVQ3DM3_USE_ITEM10, + EVQ3DM3_USE_ITEM11, // 35 + EVQ3DM3_USE_ITEM12, + EVQ3DM3_USE_ITEM13, + EVQ3DM3_USE_ITEM14, + EVQ3DM3_USE_ITEM15, + + EVQ3DM3_ITEM_RESPAWN, + EVQ3DM3_ITEM_POP, + EVQ3DM3_PLAYER_TELEPORT_IN, + EVQ3DM3_PLAYER_TELEPORT_OUT, // 43 + + // above same as q3 + + EVQ3DM3_GRENADE_BOUNCE, // eventParm will be the soundindex + + EVQ3DM3_GENERAL_SOUND, + EVQ3DM3_GLOBAL_SOUND, // no attenuation + + // not defined in protocol 43 + //EVQ3DM3_GLOBAL_TEAM_SOUND, + + EVQ3DM3_BULLET_HIT_FLESH, // 47 + EVQ3DM3_BULLET_HIT_WALL, + + EVQ3DM3_MISSILE_HIT, // 49 + EVQ3DM3_MISSILE_MISS, // 50 + + // not defined in protocol 43 + //EVQ3DM3_MISSILE_MISS_METAL, + + EVQ3DM3_RAILTRAIL, // 51 + EVQ3DM3_SHOTGUN, + EVQ3DM3_BULLET, // otherEntity is the shooter + + EVQ3DM3_PAIN, // 54 + EVQ3DM3_DEATH1, + EVQ3DM3_DEATH2, // 56 + EVQ3DM3_DEATH3, + EVQ3DM3_OBITUARY, // 58 + + EVQ3DM3_POWERUP_QUAD, // 59 + EVQ3DM3_POWERUP_BATTLESUIT, + EVQ3DM3_POWERUP_REGEN, + + EVQ3DM3_GIB_PLAYER, // gib a previously living player + EVQ3DM3_SCOREPLUM, // score plum + +//#ifdef MISSIONPACK + EVQ3DM3_PROXIMITY_MINE_STICK, // 64 + EVQ3DM3_PROXIMITY_MINE_TRIGGER, + EVQ3DM3_KAMIKAZE, // kamikaze explodes + EVQ3DM3_OBELISKEXPLODE, // obelisk explodes + EVQ3DM3_OBELISKPAIN, // obelisk is in pain + EVQ3DM3_INVUL_IMPACT, // invulnerability sphere impact + EVQ3DM3_JUICED, // invulnerability juiced effect + EVQ3DM3_LIGHTNINGBOLT, // lightning bolt bounced of invulnerability sphere +//#endif + + EVQ3DM3_DEBUG_LINE, // 72 + EVQ3DM3_STOPLOOPINGSOUND, + EVQ3DM3_TAUNT, + EVQ3DM3_TAUNT_YES, + EVQ3DM3_TAUNT_NO, + EVQ3DM3_TAUNT_FOLLOWME, + EVQ3DM3_TAUNT_GETFLAG, + EVQ3DM3_TAUNT_GUARDBASE, + EVQ3DM3_TAUNT_PATROL + +} entity_event_q3dm3_t; + #define EVCPMA_FIRE_GRAPPLE 67 #define EVCPMA_TAUNT 78 @@ -1256,11 +1377,13 @@ typedef struct gitem_s { // included in both the game dll and the client extern gitem_t bg_itemlist[]; // ql dm 90 extern const gitem_t bg_itemlistQ3[]; +extern const gitem_t bg_itemlistQ3_125p[]; extern const gitem_t bg_itemlistCpma[]; extern const gitem_t bg_itemlistQldm73[]; extern const gitem_t bg_itemlistQldm91[]; extern int bg_numItems; extern int bg_numItemsQ3; +extern int bg_numItemsQ3_125p; extern int bg_numItemsCpma; extern int bg_numItemsQldm73; extern int bg_numItemsQldm91; diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 08f837a..e55ce40 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -71,7 +71,8 @@ FIXME 2019-12-07 test other uses of longjmp(): jpeg #include "../client/cl_console.h" #endif -int demo_protocols[NUM_DEMO_PROTOCOLS] = { 66, 67, 68, 69, 70, 71, 73, 90, 91 }; // 69 ? oa? +//FIXME protocols between 43 and 48: do they all exist? +int demo_protocols[NUM_DEMO_PROTOCOLS] = { 43, 44, 45, 46, 47, 48, 66, 67, 68, 69, 70, 71, 73, 90, 91 }; // 69 ? oa? #define MAX_NUM_ARGVS 50 diff --git a/code/qcommon/msg.c b/code/qcommon/msg.c index 7590a43..4872d40 100644 --- a/code/qcommon/msg.c +++ b/code/qcommon/msg.c @@ -22,6 +22,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "q_shared.h" #include "qcommon.h" +#define BitValue(data, pos) ((data >> pos) & 1) +#define BitSet(data, pos) (data |= (1 << pos)) +#define BitClear(data, pos) (data &= ~(1 << pos)) + static huffman_t msgHuff; static qboolean msgInit = qfalse; @@ -153,23 +157,89 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) { return; } - if ( bits == 8 ) { - msg->data[msg->cursize] = value; - msg->cursize += 1; - msg->bit += 8; - } else if ( bits == 16 ) { - short temp = value; - - CopyLittleShort( &msg->data[msg->cursize], &temp ); - msg->cursize += 2; - msg->bit += 16; - } else if ( bits==32 ) { - CopyLittleLong( &msg->data[msg->cursize], &value ); - msg->cursize += 4; - msg->bit += 32; + if (com_protocol->integer > 48) { + if ( bits == 8 ) { + msg->data[msg->cursize] = value; + msg->cursize += 1; + msg->bit += 8; + } else if ( bits == 16 ) { + short temp = value; + + CopyLittleShort( &msg->data[msg->cursize], &temp ); + msg->cursize += 2; + msg->bit += 16; + } else if ( bits==32 ) { + CopyLittleLong( &msg->data[msg->cursize], &value ); + msg->cursize += 4; + msg->bit += 32; + } else { + MSG_Error(ERR_DROP, "can't write %d bits", bits); + return; + } } else { - MSG_Error(ERR_DROP, "can't write %d bits", bits); - return; + if (bits > 32) { + MSG_Error(ERR_DROP, "can't write %d bits (more than 32)", bits); + return; + } + + // data is written as little-endian + byte ldata[4]; + const byte *p; + + p = (byte *)&value; + +#if defined( Q3_BIG_ENDIAN ) + ldata[0] = p[3]; + ldata[1] = p[2]; + ldata[2] = p[1]; + ldata[3] = p[0]; +#else + ldata[0] = p[0]; + ldata[1] = p[1]; + ldata[2] = p[2]; + ldata[3] = p[3]; +#endif + + //FIXME special case where bit count aligns to byte boundary + + //Com_Printf(" x writebits %d %d, current bit %d\n", bits, value, msg->bit % 8); + + for (i = 0; i < bits; i++) { + byte *in, *out; + int inIndex, outIndex; + + out = &msg->data[msg->cursize]; + outIndex = msg->bit % 8; + inIndex = i % 8; + + if (i < 8) { + in = &ldata[0]; + } else if (i < 16) { + in = &ldata[1]; + } else if (i < 24) { + in = &ldata[2]; + } else { + in = &ldata[3]; + } + + if (BitValue(*in, inIndex)) { + BitSet(*out, outIndex); + } else { + BitClear(*out, outIndex); + } + + msg->bit += 1; + msg->cursize = msg->bit >> 3; + } + //FIXME declare variables above + //FIXME + + if (0) { + MSG_Error(ERR_DROP, "FIXME write %d bits", bits); + return; + } + //msg->bit += bits; + //msg->cursize = msg->bit >> 3; } } else { value &= (0xffffffff >> (32 - bits)); @@ -227,31 +297,76 @@ int MSG_ReadBits( msg_t *msg, int bits ) { return 0; } - if(bits==8) - { - value = msg->data[msg->readcount]; - msg->readcount += 1; - msg->bit += 8; - } - else if(bits==16) - { - short temp; - - CopyLittleShort(&temp, &msg->data[msg->readcount]); - value = temp; - msg->readcount += 2; - msg->bit += 16; - } - else if(bits==32) - { - CopyLittleLong(&value, &msg->data[msg->readcount]); - msg->readcount += 4; - msg->bit += 32; + //FIXME use real_protocol cvar? + if (com_protocol->integer > 48) { + if(bits==8) + { + value = msg->data[msg->readcount]; + msg->readcount += 1; + msg->bit += 8; + } + else if(bits==16) + { + short temp; + + CopyLittleShort(&temp, &msg->data[msg->readcount]); + value = temp; + msg->readcount += 2; + msg->bit += 16; + } + else if(bits==32) + { + CopyLittleLong(&value, &msg->data[msg->readcount]); + msg->readcount += 4; + msg->bit += 32; + } + else + { + MSG_Error(ERR_DROP, "can't read %d bits", bits); + return 0; + } } else { - MSG_Error(ERR_DROP, "can't read %d bits", bits); - return 0; + if (bits > 32) { + MSG_Error(ERR_DROP, "can't read %d bits (more than 32)", bits); + return 0; + } + + //Com_Printf("ppp %d\n", com_protocol->integer); + + //FIXME declare variables above + + // from Uber Demo Tools + uint64_t readBits = *(const uint64_t*)&msg->data[msg->readcount]; + +#if defined( Q3_BIG_ENDIAN ) + { + uint64_t tmp = readBits; + byte *to = (byte *)&readBits; + byte *from = (byte *)&tmp; + + to[0] = from[7]; + to[1] = from[6]; + to[2] = from[5]; + to[3] = from[4]; + + to[4] = from[3]; + to[5] = from[2]; + to[6] = from[1]; + to[7] = from[0]; + } +#endif + const uint64_t bitPosition = (uint64_t)msg->bit & 7; + const uint64_t diff = 64 - (uint64_t)bits; + readBits >>= bitPosition; + readBits <<= diff; + readBits >>= diff; + value = (int32_t)readBits; + msg->bit += bits; + msg->readcount = msg->bit >> 3; + + //Com_Printf("^3bits (%d): %d readcount: %d value %d\n", bits, msg->bit, msg->readcount, value); } } else { nbits = 0; @@ -1090,12 +1205,415 @@ netField_t entityStateFieldsQ3[] = { NETF(frame), 16 }, }; +// from Uber Demo Tools +netField_t entityStateFieldsQ3dm43[] = +{ + { NETF(eType), 8 }, + { NETF(eFlags), 16 }, + { NETF(pos.trType), 8 }, + { NETF(pos.trTime), 32 }, + { NETF(pos.trDuration), 32 }, + { NETF(pos.trBase[0]), 0 }, + { NETF(pos.trBase[1]), 0 }, + { NETF(pos.trBase[2]), 0 }, + { NETF(pos.trDelta[0]), 0 }, + { NETF(pos.trDelta[1]), 0 }, + { NETF(pos.trDelta[2]), 0 }, + { NETF(apos.trType), 8 }, + { NETF(apos.trTime), 32 }, + { NETF(apos.trDuration), 32 }, + { NETF(apos.trBase[0]), 0 }, + { NETF(apos.trBase[1]), 0 }, + { NETF(apos.trBase[2]), 0 }, + { NETF(apos.trDelta[0]), 0 }, + { NETF(apos.trDelta[1]), 0 }, + { NETF(apos.trDelta[2]), 0 }, + { NETF(time), 32 }, + { NETF(time2), 32 }, + { NETF(origin[0]), 0 }, + { NETF(origin[1]), 0 }, + { NETF(origin[2]), 0 }, + { NETF(origin2[0]), 0 }, + { NETF(origin2[1]), 0 }, + { NETF(origin2[2]), 0 }, + { NETF(angles[0]), 0 }, + { NETF(angles[1]), 0 }, + { NETF(angles[2]), 0 }, + { NETF(angles2[0]), 0 }, + { NETF(angles2[1]), 0 }, + { NETF(angles2[2]), 0 }, + { NETF(otherEntityNum), 10 }, + { NETF(otherEntityNum2), 10 }, + { NETF(groundEntityNum), 10 }, + { NETF(loopSound), 8 }, + { NETF(constantLight), 32 }, + { NETF(modelindex), 8 }, + { NETF(modelindex2), 8 }, + { NETF(frame), 16 }, + { NETF(clientNum), 8 }, + { NETF(solid), 24 }, + { NETF(event), 10 }, + { NETF(eventParm), 8 }, + { NETF(powerups), 16 }, + { NETF(weapon), 8 }, + { NETF(legsAnim), 8 }, + { NETF(torsoAnim), 8 }, +}; + +netField_t entityStateFieldsQ3dm46[] = +{ + { NETF(eType), 8 }, + { NETF(eFlags), 19 }, // Changed from 16 to 19... + { NETF(pos.trType), 8 }, + { NETF(pos.trTime), 32 }, + { NETF(pos.trDuration), 32 }, + { NETF(pos.trBase[0]), 0 }, + { NETF(pos.trBase[1]), 0 }, + { NETF(pos.trBase[2]), 0 }, + { NETF(pos.trDelta[0]), 0 }, + { NETF(pos.trDelta[1]), 0 }, + { NETF(pos.trDelta[2]), 0 }, + { NETF(apos.trType), 8 }, + { NETF(apos.trTime), 32 }, + { NETF(apos.trDuration), 32 }, + { NETF(apos.trBase[0]), 0 }, + { NETF(apos.trBase[1]), 0 }, + { NETF(apos.trBase[2]), 0 }, + { NETF(apos.trDelta[0]), 0 }, + { NETF(apos.trDelta[1]), 0 }, + { NETF(apos.trDelta[2]), 0 }, + { NETF(time), 32 }, + { NETF(time2), 32 }, + { NETF(origin[0]), 0 }, + { NETF(origin[1]), 0 }, + { NETF(origin[2]), 0 }, + { NETF(origin2[0]), 0 }, + { NETF(origin2[1]), 0 }, + { NETF(origin2[2]), 0 }, + { NETF(angles[0]), 0 }, + { NETF(angles[1]), 0 }, + { NETF(angles[2]), 0 }, + { NETF(angles2[0]), 0 }, + { NETF(angles2[1]), 0 }, + { NETF(angles2[2]), 0 }, + { NETF(otherEntityNum), 10 }, + { NETF(otherEntityNum2), 10 }, + { NETF(groundEntityNum), 10 }, + { NETF(loopSound), 8 }, + { NETF(constantLight), 32 }, + { NETF(modelindex), 8 }, + { NETF(modelindex2), 8 }, + { NETF(frame), 16 }, + { NETF(clientNum), 8 }, + { NETF(solid), 24 }, + { NETF(event), 10 }, + { NETF(eventParm), 8 }, + { NETF(powerups), 16 }, + { NETF(weapon), 8 }, + { NETF(legsAnim), 8 }, + { NETF(torsoAnim), 8 }, + { NETF(generic1), 8 }, //FIXME not sure if this is the same as dm_48 +}; + +// from Uber Demo Tools +netField_t entityStateFieldsQ3dm48[] = +{ + { NETF(eType), 8 }, + { NETF(eFlags), 19 }, // Changed from 16 to 19... + { NETF(pos.trType), 8 }, + { NETF(pos.trTime), 32 }, + { NETF(pos.trDuration), 32 }, + { NETF(pos.trBase[0]), 0 }, + { NETF(pos.trBase[1]), 0 }, + { NETF(pos.trBase[2]), 0 }, + { NETF(pos.trDelta[0]), 0 }, + { NETF(pos.trDelta[1]), 0 }, + { NETF(pos.trDelta[2]), 0 }, + { NETF(apos.trType), 8 }, + { NETF(apos.trTime), 32 }, + { NETF(apos.trDuration), 32 }, + { NETF(apos.trBase[0]), 0 }, + { NETF(apos.trBase[1]), 0 }, + { NETF(apos.trBase[2]), 0 }, + { NETF(apos.trDelta[0]), 0 }, + { NETF(apos.trDelta[1]), 0 }, + { NETF(apos.trDelta[2]), 0 }, + { NETF(time), 32 }, + { NETF(time2), 32 }, + { NETF(origin[0]), 0 }, + { NETF(origin[1]), 0 }, + { NETF(origin[2]), 0 }, + { NETF(origin2[0]), 0 }, + { NETF(origin2[1]), 0 }, + { NETF(origin2[2]), 0 }, + { NETF(angles[0]), 0 }, + { NETF(angles[1]), 0 }, + { NETF(angles[2]), 0 }, + { NETF(angles2[0]), 0 }, + { NETF(angles2[1]), 0 }, + { NETF(angles2[2]), 0 }, + { NETF(otherEntityNum), 10 }, + { NETF(otherEntityNum2), 10 }, + { NETF(groundEntityNum), 10 }, + { NETF(loopSound), 8 }, + { NETF(constantLight), 32 }, + { NETF(modelindex), 8 }, + { NETF(modelindex2), 8 }, + { NETF(frame), 16 }, + { NETF(clientNum), 8 }, + { NETF(solid), 24 }, + { NETF(event), 10 }, + { NETF(eventParm), 8 }, + { NETF(powerups), 16 }, + { NETF(weapon), 8 }, + { NETF(legsAnim), 8 }, + { NETF(torsoAnim), 8 }, + { NETF(generic1), 8 }, +}; + // if (int)f == f and (int)f + ( 1<<(FLOAT_INT_BITS-1) ) < ( 1 << FLOAT_INT_BITS ) // the float will be sent with FLOAT_INT_BITS, otherwise all 32 bits will be sent #define FLOAT_INT_BITS 13 #define FLOAT_INT_BIAS (1<<(FLOAT_INT_BITS-1)) +// from Uber Demo Tools +/* +The entity number has already been read from the message, which +is how the from state is identified. + +If the delta removes the entity, entityState_t->number will be set to MAX_GENTITIES-1 + +Can go from either a baseline or a previous packet_entity +*/ + +// @NOTE: Same values for dm3 and dm_48 confirmed. +static const uint8_t KnownBitMasks[32][7] = +{ + { 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xE1, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00 }, + { 0x60, 0x80, 0x00, 0x00, 0x00, 0x10, 0x00 }, + { 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 }, + { 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x60, 0x80, 0x00, 0x00, 0x01, 0x00, 0x00 }, + { 0xED, 0x07, 0x00, 0x00, 0x00, 0x80, 0x00 }, + { 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xED, 0x07, 0x00, 0x00, 0x00, 0x30, 0x00 }, + { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xE0, 0xC0, 0x00, 0x00, 0x00, 0x10, 0x00 }, + { 0x60, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00 }, + { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0xE1, 0x00, 0x00, 0x00, 0x04, 0x20, 0x00 }, + { 0xE1, 0x00, 0xC0, 0x01, 0x20, 0x20, 0x00 }, + { 0xE0, 0xC0, 0x00, 0x00, 0x01, 0x00, 0x00 }, + { 0x60, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x40, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x60, 0xC0, 0x00, 0x00, 0x01, 0x00, 0x00 }, + { 0x60, 0xC0, 0x00, 0x00, 0x00, 0x10, 0x00 }, + { 0x60, 0x80, 0x00, 0x00, 0x01, 0x00, 0x01 }, + { 0x60, 0x80, 0x00, 0x00, 0x00, 0x30, 0x00 }, + { 0xE0, 0x80, 0x00, 0x00, 0x00, 0x10, 0x00 }, + { 0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00 }, + { 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x02 }, + { 0xE0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +#if 0 +static void print_byte_bits (uint8_t number) +{ + int i; + unsigned int mask; + + // 1000 0000 0000 0000 0000 0000 0000 0000 + mask = 1 << 7; + + for (i = 0; i < 8; i++) { + Com_Printf("%c", number & mask ? '1' : '0'); + //if (i > 0 && i % 4 == 3) { + // Com_Printf(" "); + //} + + mask >>= 1; + } +} +#endif + +void MSG_WriteDeltaEntityDM3( msg_t *msg, struct entityState_s *from, struct entityState_s *to, + qboolean force ) { + int i, j, lc; + int numFields; + netField_t *field; + int trunc; + float fullFloat; + int *fromF, *toF; + int32_t maskIndex; + uint8_t bitMask[7] = { 0, 0, 0, 0, 0, 0, 0 }; // 50-51 bits used only + qboolean knownBitMaskFound = qfalse; + + if (com_protocol->integer < 46) { + numFields = ARRAY_LEN(entityStateFieldsQ3dm43); + } else if (com_protocol->integer == 46) { + numFields = ARRAY_LEN(entityStateFieldsQ3dm46); + } else if (com_protocol->integer <= 48) { + numFields = ARRAY_LEN(entityStateFieldsQ3dm48); + } else { + Com_Printf("FIXME MSG_WriteDeltaEntityDM3 unknown numFileds for protocol %d\n", com_protocol->integer); + numFields = ARRAY_LEN(entityStateFieldsQ3dm48); + } + + // all fields should be 32 bits to avoid any compiler packing issues + // the "number" field is not part of the field list + // if this assert fails, someone added a field to the entityState_t + // struct without updating the message fields + //assert( numFields + 1 == sizeof( *from )/4 ); + + // a NULL to is a delta remove message + if ( to == NULL ) { + if ( from == NULL ) { + return; + } + MSG_WriteBits( msg, from->number, GENTITYNUM_BITS ); + MSG_WriteBits( msg, 1, 1 ); + return; + } + + if ( to->number < 0 || to->number >= MAX_GENTITIES ) { + MSG_Error (ERR_FATAL, "MSG_WriteDeltaEntityDM3: Bad entity number: %i", to->number ); + return; + } + + lc = 0; + // build the change vector as bytes so it is endien independent + if (com_protocol->integer < 48) { + field = entityStateFieldsQ3dm43; + } else if (com_protocol->integer == 48) { + field = entityStateFieldsQ3dm48; + } else { + Com_Printf("FIXME MSG_WriteDeltaEntityDM3 unknown field for protocol %d\n", com_protocol->integer); + field = entityStateFieldsQ3dm48; + } + + for ( i = 0 ; i < numFields ; i++, field++ ) { + const int32_t byteIndex = i >> 3; + const int32_t bitIndex = i & 7; + + fromF = (int *)( (byte *)from + field->offset ); + toF = (int *)( (byte *)to + field->offset ); + if ( *fromF != *toF ) { + lc = i+1; + // set bit + bitMask[byteIndex] |= (1 << bitIndex); + } else { + // clear bit + bitMask[byteIndex] &= ~(1 << bitIndex); + } + } + + if ( lc == 0 ) { + // nothing at all changed + if ( !force ) { + return; // nothing at all + } + // write two bits for no change + MSG_WriteBits( msg, to->number, GENTITYNUM_BITS ); + MSG_WriteBits( msg, 0, 1 ); // not removed + MSG_WriteBits( msg, 0, 1 ); // no delta + return; + } + + MSG_WriteBits( msg, to->number, GENTITYNUM_BITS ); + MSG_WriteBits( msg, 0, 1 ); // not removed + MSG_WriteBits( msg, 1, 1 ); // we have a delta + + // check known bit masks + for (i = 0; i < 32; i++) { + for (j = 0; j < 7; j++) { + if (bitMask[j] != KnownBitMasks[i][j]) { + break; + } + } + if (j >= 7) { + knownBitMaskFound = qtrue; + break; + } + } + + //if (i >= 0x1F) { + if (!knownBitMaskFound) { + maskIndex = 0x1F; + } else { + maskIndex = i; + } + + MSG_WriteBits(msg, maskIndex, 5); + + if (maskIndex == 0x1F) { + for (i = 0; i < 6; i++) { + MSG_WriteBits(msg, bitMask[i], 8); + } + + //FIXME what is condisdered Dm3 ? + if (com_protocol->integer < 46) { + MSG_WriteBits(msg, bitMask[6], 2); + } else { + MSG_WriteBits(msg, bitMask[6], 3); + } + } + + if (com_protocol->integer < 46) { + field = entityStateFieldsQ3dm43; + } else if (com_protocol->integer == 46) { + field = entityStateFieldsQ3dm46; + } else if (com_protocol->integer <= 48) { + field = entityStateFieldsQ3dm48; + } else { + Com_Printf("FIXME MSG_WriteDeltaEntityDM3 unknown field for protocol %d\n", com_protocol->integer); + field = entityStateFieldsQ3dm48; + } + + for ( i = 0; i < lc ; i++, field++ ) { + const int32_t byteIndex = i >> 3; + const int32_t bitIndex = i & 7; + + fromF = (int *)( (byte *)from + field->offset ); + toF = (int *)( (byte *)to + field->offset ); + + //if ( *fromF == *toF ) { + // MSG_WriteBits( msg, 0, 1 ); // no change + // continue; + //} + + if ((bitMask[byteIndex] & (1 << bitIndex)) == 0) { + continue; + } + + if ( field->bits == 0 ) { + // float + fullFloat = *(float *)toF; + trunc = (int)fullFloat; + + if ( trunc == fullFloat && trunc + FLOAT_INT_BIAS >= 0 && + trunc + FLOAT_INT_BIAS < ( 1 << FLOAT_INT_BITS ) ) { + // send as small integer + MSG_WriteBits(msg, 0, 1); + MSG_WriteBits( msg, trunc + FLOAT_INT_BIAS, FLOAT_INT_BITS ); + } else { + // send as full floating point value + MSG_WriteBits( msg, 1, 1 ); + MSG_WriteBits( msg, *toF, 32 ); + } + } else { + MSG_WriteBits(msg, *toF, field->bits); + } + } +} + /* ================== MSG_WriteDeltaEntity @@ -1116,6 +1634,11 @@ void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entity float fullFloat; int *fromF, *toF; + if (com_protocol->integer >= 43 && com_protocol->integer <= 48) { + MSG_WriteDeltaEntityDM3(msg, from, to, force); + return; + } + //numFields = ARRAY_LEN( entityStateFields ); if (com_protocol->integer == PROTOCOL_Q3) { numFields = ARRAY_LEN(entityStateFieldsQ3); @@ -1242,6 +1765,170 @@ void MSG_WriteDeltaEntity( msg_t *msg, struct entityState_s *from, struct entity } } +// based on Uber Demo Tools information +static void MSG_ReadDeltaEntityDM3( msg_t *msg, entityState_t *from, entityState_t *to, int number) { + int32_t i; + int numFields; + netField_t *field; + int print; + int trunc; + int startBit, endBit; + int deltaFound; + + int32_t maskIndex; + uint8_t bitMask[7]; // 50-51 bits used only + + //Com_Printf("^3start: %d + %d\n", msg->readcount, msg->bit); + startBit = msg->bit; + + // check for a remove + if ( MSG_ReadBits( msg, 1 ) == 1 ) { + Com_Memset( to, 0, sizeof( *to ) ); + to->number = MAX_GENTITIES - 1; + if ( cl_shownet && ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) ) { + Com_Printf( "%3i: #%-3i remove\n", msg->readcount, number ); + } + return; + } + + // check for no delta + deltaFound = MSG_ReadBits(msg, 1); + //Com_Printf("deltaFound: %d\n", deltaFound); + if ( deltaFound == 0 ) { + *to = *from; + to->number = number; + //Com_Printf("no delta for %d\n", number); + return; + } + +#if 0 + // shownet 2/3 will interleave with other printed info, -1 will + // just print the delta records` + if ( cl_shownet && ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) ) { + print = 1; + Com_Printf( "%3i: #%-3i ", msg->readcount, to->number ); + } else { + print = 0; + } +#endif + + to->number = number; + + maskIndex = MSG_ReadBits(msg, 5); + +#if 1 + // shownet 2/3 will interleave with other printed info, -1 will + // just print the delta records` + if ( cl_shownet && ( cl_shownet->integer >= 2 || cl_shownet->integer == -1 ) ) { + print = 1; + Com_Printf( "%3i: #%-3i ", msg->readcount, to->number ); + + //Com_Printf( "%3i: #%-3i ", (msg->bit + (8 - 1)) / 8, to->number ); + //Com_Printf( "%3f: #%-3i ", (float)msg->bit / 8.0f, to->number ); + + // match quake 3 : + //Com_Printf( "%3i: #%-3i ", (msg->bit + 2) / 8, to->number ); + + //Com_Printf(" (%d) ", msg->bit); + } else { + print = 0; + } +#endif + + if (maskIndex == 0x1F) { + for (i = 0; i < 6; i++) { + bitMask[i] = (uint8_t)MSG_ReadBits(msg, 8); + } + //FIXME what is considered Dm3 ? + // bitMask[6] = (u8)ReadBits(_protocol == udtProtocol::Dm3 ? 2 : 3); + + bitMask[6] = (uint8_t)MSG_ReadBits(msg, com_protocol->integer < 46 ? 2 : 3); + } else { + //Com_Printf("xxxx not 0x1F %d\n", maskIndex); + // Let's not use memcpy for 7 bytes... + for (i = 0; i < 7; i++) { + bitMask[i] = KnownBitMasks[maskIndex][i]; + } + } + +#if 0 + for (i = 0; i < 7; i++) { + Com_Printf("bitMask[%d] ", i); + print_byte_bits(bitMask[i]); + Com_Printf("\n"); + } + + Com_Printf("\n"); +#endif + + if (com_protocol->integer < 46) { + numFields = ARRAY_LEN(entityStateFieldsQ3dm43); + field = entityStateFieldsQ3dm43; + } else if (com_protocol->integer == 46) { + numFields = ARRAY_LEN(entityStateFieldsQ3dm46); + field = entityStateFieldsQ3dm46; + } else if (com_protocol->integer <= 48) { + numFields = ARRAY_LEN(entityStateFieldsQ3dm48); + field = entityStateFieldsQ3dm48; + } else { + //FIXME + Com_Printf("FIXME ReadDeltaEntityDm3 unknown numFields and field for protocol %d\n", com_protocol->integer); + numFields = ARRAY_LEN(entityStateFieldsQ3dm48); + field = entityStateFieldsQ3dm48; + } + + //Com_Printf("numFields %d\n", numFields); + if ( print ) { + Com_Printf(" "); + } + + for (i = 0; i < numFields; i++, field++) { + const int32_t *fromF = (int32_t *)((uint8_t *)from + field->offset); + int32_t *toF = (int32_t *)((uint8_t *)to + field->offset); + + const int32_t byteIndex = i >> 3; + const int32_t bitIndex = i & 7; + if ((bitMask[byteIndex] & (1 << bitIndex)) == 0) { + *toF = *fromF; + continue; + } + + //Com_Printf("fname %s\n", field->name); + + if (field->bits == 0) { + // float + if ( MSG_ReadBits( msg, 1 ) == 0 ) { + // integral float + trunc = MSG_ReadBits( msg, FLOAT_INT_BITS ); + // bias to allow equal parts positive and negative + trunc -= FLOAT_INT_BIAS; + *(float *)toF = trunc; + if ( print ) { + Com_Printf( "%s:%i ", field->name, trunc ); + } + } else { + // full floating point value + *toF = MSG_ReadBits( msg, 32 ); + if ( print ) { + Com_Printf( "%s:%f ", field->name, *(float *)toF ); + } + } + } else { + *toF = MSG_ReadBits(msg, field->bits); + if ( print ) { + Com_Printf( "%s:%i ", field->name, *toF ); + } + } + } + + if ( print ) { + endBit = msg->bit; + Com_Printf( " (%i bits)\n", endBit - startBit ); + } + + return; +} + /* ================== MSG_ReadDeltaEntity @@ -1270,6 +1957,12 @@ void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, return; } + if (com_protocol->integer >= 43 && com_protocol->integer <= 48) { + //Com_Printf("dm3....\n"); + MSG_ReadDeltaEntityDM3(msg, from, to, number); + return; + } + if ( msg->bit == 0 ) { startBit = msg->readcount * 8 - GENTITYNUM_BITS; } else { @@ -1310,6 +2003,7 @@ void MSG_ReadDeltaEntity( msg_t *msg, entityState_t *from, entityState_t *to, lc = MSG_ReadByte(msg); if (lc > numFields || lc < 0) { + Com_Printf("com_protocol %d\n", com_protocol->integer); MSG_Error(ERR_DROP, "invalid entityState field count: %d, numFields %d", lc, numFields); //Com_Printf("^3invalid entityState field count: %d, numFields %d", lc, numFields); return; @@ -1599,6 +2293,325 @@ netField_t playerStateFieldsQ3[] = { PSF(loopSound), 16 }, }; +// from Uber Demo Tools +netField_t playerStateFieldsQ3dm43[] = +{ + { PSF(commandTime), 32 }, + { PSF(pm_type), 8 }, + { PSF(bobCycle), 8 }, + { PSF(pm_flags), 16 }, + { PSF(pm_time), 16 }, + { PSF(origin[0]), 0 }, + { PSF(origin[1]), 0 }, + { PSF(origin[2]), 0 }, + { PSF(velocity[0]), 0 }, + { PSF(velocity[1]), 0 }, + { PSF(velocity[2]), 0 }, + { PSF(weaponTime), 16 }, + { PSF(gravity), 16 }, + { PSF(speed), 16 }, + { PSF(delta_angles[0]), 16 }, + { PSF(delta_angles[1]), 16 }, + { PSF(delta_angles[2]), 16 }, + { PSF(groundEntityNum), 10 }, + { PSF(legsTimer), 8 }, + { PSF(torsoTimer), 12 }, + { PSF(legsAnim), 8 }, + { PSF(torsoAnim), 8 }, + { PSF(movementDir), 4 }, + { PSF(eFlags), 16 }, + { PSF(eventSequence), 16 }, + { PSF(events[0]), 8 }, + { PSF(events[1]), 8 }, + { PSF(eventParms[0]), 8 }, + { PSF(eventParms[1]), 8 }, + { PSF(externalEvent), 8 }, + { PSF(externalEventParm), 8 }, + { PSF(clientNum), 8 }, + { PSF(weapon), 5 }, + { PSF(weaponstate), 4 }, + { PSF(viewangles[0]), 0 }, + { PSF(viewangles[1]), 0 }, + { PSF(viewangles[2]), 0 }, + { PSF(viewheight), 8 }, + { PSF(damageEvent), 8 }, + { PSF(damageYaw), 8 }, + { PSF(damagePitch), 8 }, + { PSF(damageCount), 8 }, + { PSF(grapplePoint[0]), 0 }, + { PSF(grapplePoint[1]), 0 }, + { PSF(grapplePoint[2]), 0 }, +}; + +netField_t playerStateFieldsQ3dm46[] = +{ + { PSF(commandTime), 32 }, + { PSF(pm_type), 8 }, + { PSF(bobCycle), 8 }, + { PSF(pm_flags), 16 }, + { PSF(pm_time), 16 }, //FIXME maybe negative like dm_48 ? + { PSF(origin[0]), 0 }, + { PSF(origin[1]), 0 }, + { PSF(origin[2]), 0 }, + { PSF(velocity[0]), 0 }, + { PSF(velocity[1]), 0 }, + { PSF(velocity[2]), 0 }, + { PSF(weaponTime), 16 }, //FIXME maybe negative like dm_48? + { PSF(gravity), 16 }, + { PSF(speed), 16 }, + { PSF(delta_angles[0]), 16 }, + { PSF(delta_angles[1]), 16 }, + { PSF(delta_angles[2]), 16 }, + { PSF(groundEntityNum), 10 }, + { PSF(legsTimer), 8 }, + { PSF(torsoTimer), 12 }, + { PSF(legsAnim), 8 }, + { PSF(torsoAnim), 8 }, + { PSF(movementDir), 4 }, + { PSF(eFlags), 16 }, + { PSF(eventSequence), 16 }, + { PSF(events[0]), 8 }, + { PSF(events[1]), 8 }, + { PSF(eventParms[0]), 8 }, + { PSF(eventParms[1]), 8 }, + { PSF(externalEvent), 10 }, //FIXME maybe changed to 10 like dm_48 ? + { PSF(externalEventParm), 8 }, + { PSF(clientNum), 8 }, + { PSF(weapon), 5 }, + { PSF(weaponstate), 4 }, + { PSF(viewangles[0]), 0 }, + { PSF(viewangles[1]), 0 }, + { PSF(viewangles[2]), 0 }, + { PSF(viewheight), -8 }, // now negative? + { PSF(damageEvent), 8 }, + { PSF(damageYaw), 8 }, + { PSF(damagePitch), 8 }, + { PSF(damageCount), 8 }, + { PSF(grapplePoint[0]), 0 }, + { PSF(grapplePoint[1]), 0 }, + { PSF(grapplePoint[2]), 0 }, + { PSF(jumppad_ent), 10 }, // new + { PSF(loopSound), 16 }, // new +}; + +// from Uber Demo Tools +netField_t playerStateFieldsQ3dm48[] = +{ + { PSF(commandTime), 32 }, + { PSF(pm_type), 8 }, + { PSF(bobCycle), 8 }, + { PSF(pm_flags), 16 }, + { PSF(pm_time), -16 }, //FIXME wc negative? + { PSF(origin[0]), 0 }, + { PSF(origin[1]), 0 }, + { PSF(origin[2]), 0 }, + { PSF(velocity[0]), 0 }, + { PSF(velocity[1]), 0 }, + { PSF(velocity[2]), 0 }, + { PSF(weaponTime), -16 }, //FIXME wc negative? + { PSF(gravity), 16 }, + { PSF(speed), 16 }, + { PSF(delta_angles[0]), 16 }, + { PSF(delta_angles[1]), 16 }, + { PSF(delta_angles[2]), 16 }, + { PSF(groundEntityNum), 10 }, + { PSF(legsTimer), 8 }, + { PSF(torsoTimer), 12 }, + { PSF(legsAnim), 8 }, + { PSF(torsoAnim), 8 }, + { PSF(movementDir), 4 }, + { PSF(eFlags), 16 }, + { PSF(eventSequence), 16 }, + { PSF(events[0]), 8 }, + { PSF(events[1]), 8 }, + { PSF(eventParms[0]), 8 }, + { PSF(eventParms[1]), 8 }, + { PSF(externalEvent), 10 }, // Changed from 8 to 10... + { PSF(externalEventParm), 8 }, + { PSF(clientNum), 8 }, + { PSF(weapon), 5 }, + { PSF(weaponstate), 4 }, + { PSF(viewangles[0]), 0 }, + { PSF(viewangles[1]), 0 }, + { PSF(viewangles[2]), 0 }, + { PSF(viewheight), -8 }, //FIXME wc negative? + { PSF(damageEvent), 8 }, + { PSF(damageYaw), 8 }, + { PSF(damagePitch), 8 }, + { PSF(damageCount), 8 }, + { PSF(grapplePoint[0]), 0 }, + { PSF(grapplePoint[1]), 0 }, + { PSF(grapplePoint[2]), 0 }, + { PSF(jumppad_ent), 10 }, // New in dm_48. + { PSF(loopSound), 16 }, // New in dm_48. + { PSF(generic1), 8 }, // New in dm_48. +}; + + +void MSG_WriteDeltaPlayerstateDM3( msg_t *msg, struct playerState_s *from, struct playerState_s *to ) { + int i; + playerState_t dummy; + int statsbits; + int persistantbits; + int ammobits; + int powerupbits; + int numFields; + netField_t *field; + int *fromF, *toF; + float fullFloat; + int trunc; + + if (!from) { + from = &dummy; + Com_Memset (&dummy, 0, sizeof(dummy)); + } + + //FIXME double check 45, and what about 47? + if (com_protocol->integer < 46) { + numFields = ARRAY_LEN(playerStateFieldsQ3dm43); + } else if (com_protocol->integer == 46) { + numFields = ARRAY_LEN(playerStateFieldsQ3dm46); + } else if (com_protocol->integer <= 48) { + numFields = ARRAY_LEN( playerStateFieldsQ3dm48); + } else { + //FIXME + Com_Printf("FIXME MSG_WriteDeltaPlayerstateDM3 unknown numFields for protocol %d\n", com_protocol->integer); + numFields = ARRAY_LEN(playerStateFieldsQ3dm48); + } + + if (com_protocol->integer < 46) { + field = playerStateFieldsQ3dm43; + } else if (com_protocol->integer == 46) { + field = playerStateFieldsQ3dm46; + } else if (com_protocol->integer <= 48) { + field = playerStateFieldsQ3dm48; + } else { // also qldm 73 + //FIXME + Com_Printf("FIXME MSG_WriteDeltaPlayerstateDM3 unknown field for protocol %d\n", com_protocol->integer); + field = playerStateFieldsQ3dm48; + } + + for (i = 0; i < numFields; i++, field++) { + fromF = (int *)( (byte *)from + field->offset ); + toF = (int *)( (byte *)to + field->offset ); + + if (*fromF == *toF) { + MSG_WriteBits(msg, 0, 1); // no change + continue; + } + + MSG_WriteBits(msg, 1, 1); // changed + + if (field->bits == 0) { + // float + fullFloat = *(float *)toF; + trunc = (int)fullFloat; + + if ( trunc == fullFloat && trunc + FLOAT_INT_BIAS >= 0 && + trunc + FLOAT_INT_BIAS < ( 1 << FLOAT_INT_BITS ) ) { + // send as small integer + MSG_WriteBits( msg, 0, 1 ); + MSG_WriteBits( msg, trunc + FLOAT_INT_BIAS, FLOAT_INT_BITS ); + + } else { + // send as full floating point value + MSG_WriteBits( msg, 1, 1 ); + MSG_WriteBits( msg, *toF, 32 ); + } + + } else { + // integer + MSG_WriteBits( msg, *toF, field->bits ); + } + } + + // + // send the arrays + // + statsbits = 0; + for (i=0 ; istats[i] != from->stats[i]) { + statsbits |= 1<persistant[i] != from->persistant[i]) { + persistantbits |= 1<ammo[i] != from->ammo[i]) { + ammobits |= 1<powerups[i] != from->powerups[i]) { + powerupbits |= 1<stats[i], -16); + } + } else { + MSG_WriteBits( msg, 0, 1 ); // no change + } + + + if ( persistantbits ) { + MSG_WriteBits( msg, 1, 1 ); // changed + MSG_WriteBits( msg, persistantbits, 16 ); + for (i=0 ; ipersistant[i], 16); + } + } else { + MSG_WriteBits( msg, 0, 1 ); // no change + } + + + if ( ammobits ) { + MSG_WriteBits( msg, 1, 1 ); // changed + MSG_WriteBits( msg, ammobits, 16 ); + for (i=0 ; iammo[i], -16); + } + } else { + MSG_WriteBits( msg, 0, 1 ); // no change + } + + + if ( powerupbits ) { + MSG_WriteBits( msg, 1, 1 ); // changed + MSG_WriteBits( msg, powerupbits, 16 ); + for (i=0 ; ipowerups[i], 32 ); + } + } else { + MSG_WriteBits( msg, 0, 1 ); // no change + } +} + /* ============= MSG_WriteDeltaPlayerstate @@ -1618,6 +2631,11 @@ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct p float fullFloat; int trunc, lc; + if (com_protocol->integer >= 43 && com_protocol->integer <= 48) { + MSG_WriteDeltaPlayerstateDM3(msg, from, to); + return; + } + if (!from) { from = &dummy; Com_Memset (&dummy, 0, sizeof(dummy)); @@ -1773,6 +2791,155 @@ void MSG_WriteDeltaPlayerstate( msg_t *msg, struct playerState_s *from, struct p } } +static void MSG_ReadDeltaPlayerstateDM3 (msg_t *msg, playerState_t *from, playerState_t *to ) { + int i; + netField_t *field; + int numFields; + int startBit, endBit; + int print; + int trunc; + int32_t mask; + + // already done +#if 0 + if ( !from ) { + from = &dummy; + Com_Memset( &dummy, 0, sizeof( dummy ) ); + } + *to = *from; +#endif + + startBit = msg->bit; + + // shownet 2/3 will interleave with other printed info, -2 will + // just print the delta records + if ( cl_shownet && ( cl_shownet->integer >= 2 || cl_shownet->integer == -2 ) ) { + print = 1; + Com_Printf( "%3i: playerstate ", msg->readcount ); + } else { + print = 0; + } + + if (com_protocol->integer < 46) { + numFields = ARRAY_LEN(playerStateFieldsQ3dm43); + field = playerStateFieldsQ3dm43; + } else if (com_protocol->integer == 46) { + numFields = ARRAY_LEN(playerStateFieldsQ3dm46); + field = playerStateFieldsQ3dm46; + } else if (com_protocol->integer <= 48) { + numFields = ARRAY_LEN(playerStateFieldsQ3dm48); + field = playerStateFieldsQ3dm48; + } else { + //FIXME + Com_Printf("FIXME MSG_ReadDeltaPlayerstateDM3 unknown numFields and field for protocol %d\n", com_protocol->integer); + numFields = ARRAY_LEN(playerStateFieldsQ3dm48); + field = playerStateFieldsQ3dm48; + } + + for (i = 0; i < numFields; i++, field++) { + int32_t *toF; + + if (!MSG_ReadBits(msg, 1)) { + //Com_Printf("no %s\n", field->name); + continue; + } + + toF = (int32_t *)((uint8_t *)to + field->offset); + //Com_Printf("field->name %s\n", field->name); + + // *toF = ReadField(filed->bits); + + if (field->bits == 0) { + // float + if ( MSG_ReadBits( msg, 1 ) == 0 ) { + // integral float + trunc = MSG_ReadBits( msg, FLOAT_INT_BITS ); + // bias to allow equal parts positive and negative + trunc -= FLOAT_INT_BIAS; + *(float *)toF = trunc; + if ( print ) { + Com_Printf( "%s:%i ", field->name, trunc ); + } + } else { + // full floating point value + *toF = MSG_ReadBits( msg, 32 ); + if ( print ) { + Com_Printf( "%s:%f ", field->name, *(float *)toF ); + } + } + } else { + *toF = MSG_ReadBits(msg, field->bits); + if ( print ) { + Com_Printf( "%s:%i ", field->name, *toF ); + } + } + } + + //FIXME MSG_ReadShort() instead of MSG_ReadBits(16) ? short() has overflow check to return -1 + + //Com_Printf( " temppppp (%i bits)\n", msg->bit - startBit ); + + //Com_Printf(" next bit: %d\n", MSG_ReadBits(msg, 1)); + + // Stats array. + if (MSG_ReadBits(msg, 1)) { + LOG("PS_STATS"); + + mask = MSG_ReadBits(msg, 16); + for (i = 0; i < MAX_STATS; i++) { + if ((mask & (1 << i)) != 0) { // bit set? + // Stats can be negative + // to->stats[i] = ReadSignedShort(); + // ReadBits(-16) + to->stats[i] = MSG_ReadBits(msg, -16); + } + } + } + + // Persistent array. + if (MSG_ReadBits(msg, 1)) { + LOG("PS_PERSISTANT"); + + mask = MSG_ReadBits(msg, 16); + for (i = 0; i < MAX_PERSISTANT; i++) { + if ((mask & (1 << i)) != 0) { // bit set? + // @TODO: Can those be negative too? + to->persistant[i] = MSG_ReadBits(msg, 16); + } + } + } + + // Ammo array. + if (MSG_ReadBits(msg, 1)) { + LOG("PS_AMMO"); + + mask = MSG_ReadBits(msg, 16); + for (i = 0; i < MAX_WEAPONS; i++) { + if ((mask & (1 << i)) != 0) { // bit set? + // @TODO: Can those be negative too? + to->ammo[i] = MSG_ReadBits(msg, 16); + } + } + } + + // Power-ups array. + if (MSG_ReadBits(msg, 1)) { + LOG("PS_POWERUPS"); + + mask = MSG_ReadBits(msg, 16); + for (i = 0; i < MAX_POWERUPS; i++) { + if ((mask & (1 << i)) != 0) { // bit set? + // Yep, we read 32 bits. + to->powerups[i] = MSG_ReadBits(msg, 32); + } + } + } + + if ( print ) { + endBit = msg->bit; + Com_Printf( " (%i bits)\n", endBit - startBit ); + } +} /* =================== @@ -1796,6 +2963,11 @@ void MSG_ReadDeltaPlayerstate (msg_t *msg, playerState_t *from, playerState_t *t } *to = *from; + if (com_protocol->integer >= 43 && com_protocol->integer <= 48) { + MSG_ReadDeltaPlayerstateDM3(msg, from, to); + return; + } + if ( msg->bit == 0 ) { startBit = msg->readcount * 8 - GENTITYNUM_BITS; } else { diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index b4d7e2f..a1826d4 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -26,6 +26,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // q_shared.h -- included first by ALL program modules. // A user mod should never modify this file +#define PROTOCOL_Q3DEMO 43 +#define PROTOCOL_Q3ORIG 46 #define PROTOCOL_Q3 68 #define PROTOCOL_QL 91 // latest ql protocol #define SERVER_PROTOCOL 91 // devmap diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index edc6117..41d01e9 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -252,8 +252,8 @@ extern mapNames_t MapNames[]; // maintain a list of compatible protocols for demo playing // NOTE: that stuff only works with two digits protocols -// 66, 67, 68, 69, 70, 71, 73, 90, 91 -#define NUM_DEMO_PROTOCOLS 9 //FIXME err.... ARRAY_LEN() +// 43, 44, 45, 46, 47, 48, 66, 67, 68, 69, 70, 71, 73, 90, 91 +#define NUM_DEMO_PROTOCOLS 15 //FIXME err.... ARRAY_LEN() extern int demo_protocols[NUM_DEMO_PROTOCOLS]; #if !defined UPDATE_SERVER_NAME && !defined STANDALONE diff --git a/version.txt b/version.txt index b26b65d..c222b5d 100644 --- a/version.txt +++ b/version.txt @@ -1,3 +1,9 @@ +12.7test1 + +* protocol 48 (dm_48), 46 (dm3), 45 (dm3), and 43 (dm3) support +* bug fix: CS_PARTICLE index range incorrectly calculated for protocol 68 + +--------------------------------- 12.6 2024-05-22 * ioquake3 patches