PDA

View Full Version : Q3 Engine Game Bugs / JA bugs


ensiform
11-13-2004, 12:53 PM
1. (g_cmds.c) cmd_where_f should use
vtos( ent->r.currentOrigin ) in the print too show current location.
2. bot_minplayers removerandom bot bug where it kicks spectators watching them instead of the bot:

change the following in g_bot.c:

trap_SendConsoleCommand( EXEC_INSERT, va("kick \"%s\"\n", netname) );

to

trap_SendConsoleCommand( EXEC_INSERT, va("clientkick \"%d\"\n", cl->ps.clientNum));

3. Something i found in ET mod-source so credit them and me...

in g_client.c below the check for invaild pw put:

// Gordon: porting q3f flag bug fix
// If a player reconnects quickly after a disconnect, the client disconnect may never be called, thus flag can get lost in the ether
if( ent->inuse ) {
G_LogPrintf( "Forcing disconnect on active client: %i\n", ent-g_entities );
// so lets just fix up anything that should happen on a disconnect
ClientDisconnect( ent-g_entities );
}

4. bug in password checking if statement also in g_client.c:

SVF_BOT isnt set till below so change it to !isBot

5. remapShader in cg_servercmds.c is bugged, just comment out old and replace with the following if u plan on having a client also:

if ( Q_strncmp (cmd, "remapShader", 11) == 0 ) {
if (trap_Argc() == 4) {
char shader1[MAX_QPATH];
char shader2[MAX_QPATH];
Q_strncpyz(shader1, CG_Argv(1), sizeof(shader1));
Q_strncpyz(shader2, CG_Argv(2), sizeof(shader2));
trap_R_RemapShader(shader1, shader2, CG_Argv(3));
return;
}
}

6. for now from my other post Tinny showed me how too fix player sliding:
in bg_pmove.c u will find:

// If on a client then there is no friction
else if ( pm->ps->groundEntityNum < MAX_CLIENTS )
{
drop = 0;
}

comment that out and compile cgame and game i think for this too work.

Finally, if you would like me too share my /dropflag cmd drop me a pm. it should be pretty much exploit proof. i have trace in it so that if u toss it next to a wall it does not go out of level.

i also did fix the connection screen bug but i feel that i didnt need to show this now :)

also i may suggest disabling the debug cmds in g_cmds.c or u could fix em up and disable use of -1 for setsabermove :) and make a cvar too allow/disallow them.

(debugThrow, debugDropSaber, debugSetSaberMove, debugKnockMeDown, debugSetBodyAnim, debugDismemberment, and debugSaberSwitch)

razorace
11-13-2004, 06:08 PM
Just compiling your code with the "Final" Compile configuration removes the issue with the debug commands being availible to players.

ensiform
11-13-2004, 06:32 PM
exactly but my mod allows them with a cvar but some mods dont notice the ifndef final_build around them.

Tinny
11-14-2004, 02:41 PM
Thanks a lot for those Ensiform, btw what exactly is /dropflag?

ensiform
11-15-2004, 04:09 AM
heh its to toss the flag in ctf :)

ensiform
11-15-2004, 12:31 PM
Anybody know how to fix the warning that comes up from this line of code in cg_players.c:

const unsigned char savRGBA[3] = {legs.shaderRGBA[0],legs.shaderRGBA[1],legs.shaderRGBA[2]};

The warning is: warning C4204: nonstandard extension used : non-constant aggregate initializer

um ppl feel free too post your bugfixes here :). can we get this to be an announcement or sticky ?

razorace
11-15-2004, 09:12 PM
The error is due to C not liking you initizing your vec3_ts and similar data thingys when you define them. Do the following to fix this:

cg_players.c, CG_Player()

const unsigned char savRGBA[3] = {legs.shaderRGBA[0],legs.shaderRGBA[1],legs.shaderRGBA[2]};
=>
unsigned char savRGBA[3];
savRGBA[0] = legs.shaderRGBA[0];
savRGBA[1] = legs.shaderRGBA[1];
savRGBA[2] = legs.shaderRGBA[2];

ensiform
11-16-2004, 02:55 PM
that code gives this :(


warning C4132: 'savRGBA' : const object should be initialized
error C2166: l-value specifies const object
error C2166: l-value specifies const object
error C2166: l-value specifies const object

razorace
11-16-2004, 03:29 PM
My bad. Remove the "const" from the define.

EDIT: The code in the above post has been fixed to remove that problem.

ensiform
11-17-2004, 11:01 AM
ah, ty ;) trying to fix all the bugs and warnings to have a bug-free JA for RS :)

ensiform
11-17-2004, 02:27 PM
Here are some sound bugfixes:

when you use meditate/bow and saber is not out, usually with staff or dual sabers the saber off sound still plays fix:

meditate:

where it says

{//turn off second saber
just above the G_Sound add

if (ent->client->ps.weapon == WP_SABER)

same for {//turn off first
if (ent->client->ps.weapon == WP_SABER)

do the same with bow.

in g_cmds.c find the Cmd_SaberAttackCycle_f command

and look for the G_Sound 's in it and just add above them:
if (ent->client->ps.weapon == WP_SABER)

i suppose you could just have a return towards the top of the function like:

if (ent->client->ps.weapon == WP_SABER) {
return;
}

hope that helps :)

ensiform
11-20-2004, 05:37 AM
CopyToBodyQue has a bug where your custom rgb is used even when in team game. to fix:

body->s.customRGBA[0] = ent->client->ps.customRGBA[0];
body->s.customRGBA[1] = ent->client->ps.customRGBA[1];
body->s.customRGBA[2] = ent->client->ps.customRGBA[2];
body->s.customRGBA[3] = ent->client->ps.customRGBA[3];

should be:

if (g_gametype.integer >= GT_TEAM) {
switch(ent->client->sess.sessionTeam)
{
case TEAM_RED:
body->s.customRGBA[0] = 255;
body->s.customRGBA[1] = 50;
body->s.customRGBA[2] = 50;
break;
case TEAM_BLUE:
body->s.customRGBA[0] = 75;
body->s.customRGBA[1] = 75;
body->s.customRGBA[2] = 255;
break;
default:
body->s.customRGBA[0] = ent->client->ps.customRGBA[0];
body->s.customRGBA[1] = ent->client->ps.customRGBA[1];
body->s.customRGBA[2] = ent->client->ps.customRGBA[2];
body->s.customRGBA[3] = ent->client->ps.customRGBA[3];
break;
}
} else {
body->s.customRGBA[0] = ent->client->ps.customRGBA[0];
body->s.customRGBA[1] = ent->client->ps.customRGBA[1];
body->s.customRGBA[2] = ent->client->ps.customRGBA[2];
body->s.customRGBA[3] = ent->client->ps.customRGBA[3];
}

razorace
11-20-2004, 07:33 AM
Ahh, I've actually noticed that bug. Thanks for the fix!

ensiform
11-20-2004, 08:38 AM
hehe, i doubt im gonna get a reply from masterhex about the sound tracker thing though.

razorace
11-20-2004, 05:50 PM
Exactly what was the sound tracker problem again? I thought the solution was to use SoundOnEnt instead of G_Sound.

ensiform
11-20-2004, 06:25 PM
um according too hex's original post it's in CG_UpdateSoundTrackers which is in cg_view.c.

razorace
11-20-2004, 07:08 PM
What's the symptoms of the bug again?

ensiform
11-21-2004, 12:20 PM
um the sounds do not always play when flag taken/return/captures some other sounds too i believe. set your thread cutoff too show all and look back at hex's old thread.

ensiform
11-23-2004, 07:19 PM
has anyone fixed ICARUS with NPC's for MP like SP ? so that u can use scripts with npc's too be smart :) ?

razorace
11-23-2004, 09:06 PM
I have. I'm working on getting the SP maps to run as well as possible in MP. I've made great progress so far.

ensiform
11-24-2004, 05:11 AM
care too share this fixed code ? :D

razorace
11-24-2004, 09:49 AM
It's all availible on the OJP repository. The various fixes require a lot of code addtions/changes thruout the SDK so I can't just post the changes.

ensiform
11-24-2004, 10:03 AM
got a link to the ojp sdk ?

Tinny
11-27-2004, 08:58 AM
You have to use TortoiseCVS, check out How to access the OJP source in the OJP forum which is located in the Hosted forums.

ensiform
11-27-2004, 07:16 PM
did but it aint working right.

razorace
11-27-2004, 09:51 PM
Well, our repository server was down yesterday. I suggest you try again today.

ensiform
12-05-2004, 11:57 AM
dang thats too much work just too get the sdk for ojp :(

razorace
12-05-2004, 12:07 PM
You only have to do it once and after that updates are a two click process. OJP's SDK changes about daily.

ASk
12-15-2004, 08:04 AM
The warning is: warning C4204: nonstandard extension used : non-constant aggregate initializer


Just a little clarification - you may only initialize a const with a constant expression (i.e a number). The reason for this is simple - when the compiler compiles your code, the const variable is replaced by its value everywhere it appears. Initializing it with the value of another variable can't be resolved until run-time, therefore it's not an acceptable behavior.

Since the const variable must be resolved during compile time and not run-time, you get that error.

ensiform
12-17-2004, 04:00 PM
yes but that was a lucas/raven bug not one of mine :)

GangsterAngel
12-19-2004, 01:15 PM
not sure if this is a 'bug' ,
but vehicles do not open shields,

FIX:

void ShieldTouch(gentity_t *self, gentity_t *other, trace_t *trace)
{

gentity_t *owner;
owner=self->parent;

//Vehicle open shield's too! - GA
if(other->s.NPC_class == CLASS_VEHICLE) {
if(other->m_pVehicle->GA_LastRider==NULL)return;
else {
other = other->m_pVehicle->GA_LastRider;
if(!other->inuse || !other->client)return;
}
}

if (g_gametype.integer >= GT_TEAM)
{ // let teammates through
// compare the parent's team to the "other's" team
if (self->parent && ( self->parent->client) && (other->client))
{
if (OnSameTeam(self->parent, other))
{
ShieldGoNotSolid(self);
}
}
}
else
{//let the person who dropped the shield through
if (self->parent && self->parent->s.number == other->s.number)
{
ShieldGoNotSolid(self);
}
else if(InSameGang(owner,other)) ShieldGoNotSolid(self);
else if(self->genericValue13!=0 && self->genericValue13 <= other->client->pers.ShieldRank)ShieldGoNotSolid(self);//Rank based shiels
}
//NPC's who leader owns the shield

if (other && other->inuse && other->s.eType==ET_NPC){
//if(other->client->leader==owner)ShieldGoNotSolid(self);
///Not going to use the above way ,, just going to call ShieldTouch on the owner
ShieldTouch(self,other->client->leader,NULL);

}
}

GangsterAngel
12-19-2004, 01:18 PM
forgot to add. that function has GA_Lastrider in it ,
witch is a value set when someone gets on a vehicle.
so the vehicle remembers the last person on it,

also. it has my "Rank shields" in it , that have to be removed,

( Basicly u just need the top bit added too yours )

razorace
12-19-2004, 04:45 PM
By shields you mean the deployable shield item?

GangsterAngel
12-19-2004, 04:48 PM
yes i do.

razorace
03-01-2005, 02:00 PM
(Bump Question) Say, what does "remapShader" do anyway?

Wudan
03-04-2005, 12:13 PM
If I recall it tells the engine and replace all instances of shader 'a' with shader 'b', and gets cleared when a map is loaded.

razorace
03-04-2005, 02:05 PM
so, what uses it?

ensiform
07-08-2005, 08:28 PM
the actual fix for #4 of mine at the top should be:

if ( !isBot && g_needpass.integer && (strcmp(Info_ValueForKey ( userinfo, "ip" ), "localhost") != 0)) {

ensiform
07-08-2005, 08:29 PM
gloat really should only work when saber is out because it shows sparks even when weapon is not saber...

if ( ent->client->ps.weapon == WP_SABER )
{
if ( ent->client->saber[0].gloatAnim != -1 )
{
anim = ent->client->saber[0].gloatAnim;
}
else if ( ent->client->saber[1].model
&& ent->client->saber[1].model[0]
&& ent->client->saber[1].gloatAnim != -1 )
{
anim = ent->client->saber[1].gloatAnim;
}
else
{
switch ( ent->client->ps.fd.saberAnimLevel )
{
case SS_FAST:
case SS_TAVION:
anim = BOTH_VICTORY_FAST;
break;
case SS_MEDIUM:
anim = BOTH_VICTORY_MEDIUM;
break;
case SS_STRONG:
case SS_DESANN:
if ( ent->client->ps.saberHolstered )
{//turn on first
G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
}
ent->client->ps.saberHolstered = 0;
anim = BOTH_VICTORY_STRONG;
break;
case SS_DUAL:
if ( ent->client->ps.saberHolstered == 1
&& ent->client->saber[1].model
&& ent->client->saber[1].model[0] )
{//turn on second saber
G_Sound( ent, CHAN_WEAPON, ent->client->saber[1].soundOn );
}
else if ( ent->client->ps.saberHolstered == 2 )
{//turn on first
G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
}
ent->client->ps.saberHolstered = 0;
anim = BOTH_VICTORY_DUAL;
break;
case SS_STAFF:
if ( ent->client->ps.saberHolstered )
{//turn on first
G_Sound( ent, CHAN_WEAPON, ent->client->saber[0].soundOn );
}
ent->client->ps.saberHolstered = 0;
anim = BOTH_VICTORY_STAFF;
break;
}
}
break;
}
}

razorace
07-09-2005, 01:48 AM
While on the subject, has anyone else noticed bots wearing the wrong teams in team games?

Also, I have finally fixed the Hoth Bridge exploit if anyone is interested.

ensiform
07-09-2005, 05:21 PM
Originally posted by razorace
While on the subject, has anyone else noticed bots wearing the wrong teams in team games?

Also, I have finally fixed the Hoth Bridge exploit if anyone is interested.

never noticed wrong team colors...

i would like that but siege doesnt work for rs or my basejka :/

razorace
07-09-2005, 07:43 PM
It doesn't work at all?!

ensiform
07-09-2005, 09:50 PM
no, siege gives max cvars error...

actually base works for me now, for some reason all my rs cvars n **** were in base's config...

but i found a bug in cmd_kill_f with siege...


if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
return;
}
shouldnt it be this, i was playing around and when i was specing when i could move, and pressed kill and i actually died again...

if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->client->tempSpectate > level.time ) {
return;
}

razorace
07-10-2005, 12:24 PM
Well, it probably means that RS has simply added too much cvars to the game. I've heard once that it's possible to increase the limit but I've never confirmed that.

ensiform
07-10-2005, 06:52 PM
not without the engine :/ whats weird is even when i dont make any of them stored in the config either, usually that is what causes it. i suppose u could probably get away with creating another cvartable...

ensiform
07-13-2005, 07:01 PM
havent been able to fix this yet but:

bot_nochat doesnt really work. ima look into it but im not sure if it is even fixable in mod.

Edit: no i really dont think it is possible without making my own cvar.

razorace
07-13-2005, 07:42 PM
Couldn't you just alter the bot chat function so it just disables if the cvar is set.

ensiform
07-13-2005, 08:06 PM
maybe but the cvar doesnt exist in the game code. i got it working using bot_nochat2.

in ai_util.c find the function BotDoChat

just below the gentity_t *cobject; line add:

if (bot_nochat2.integer)
{
return 0;
}


you will of course need to make the cvar in g_local.h and g_main.c.

razorace
07-13-2005, 10:11 PM
Why not just use the original bot_nochat cvar?

ensiform
07-14-2005, 12:45 PM
cause it doesnt exist and it may do something else in the engine and i dont want to **** it up.

razorace
07-14-2005, 01:50 PM
Well, if it's not in the code, it's not in the code. I wouldn't worry about it. :)

ensiform
09-04-2005, 09:43 PM
grr... only send 20 clients wtf ?

comment this out in g_cmds.c DeathmatchScoreboardMessage

if (numSorted > MAX_CLIENT_SCORE_SEND)
{
numSorted = MAX_CLIENT_SCORE_SEND;
}

(MAX_CLIENT_SCORE_SEND=20)

razorace
09-05-2005, 05:41 AM
It's probably because the scoreboard are only hold 20 client's stats at once. (guessing)

ensiform
09-05-2005, 09:32 PM
should be able to hold more, i know the one in red slushie / ensimod can.

razorace
09-06-2005, 08:43 PM
*shrug* well, it was probably also done for bandwidth reasons as well but it's worth a shot. :)

zERoCooL2479
09-29-2005, 10:43 PM
more of an annoyance than a bug, but here goes:

In g_ICARUScb.c

static void Q3_LCARSText ( const char *id)
{
G_DebugPrint( WL_WARNING, "Q3_ScrollText: NOT SUPPORTED IN MP\n");
//trap_SendServerCommand( -1, va("lt \"%s\"", id));

return;
}


should be:


static void Q3_LCARSText ( const char *id)
{
// MJN - fix: typo in warning message.
G_DebugPrint( WL_WARNING, "Q3_LCARSText: NOT SUPPORTED IN MP\n");
//trap_SendServerCommand( -1, va("lt \"%s\"", id));

return;
}

razorace
09-29-2005, 11:22 PM
Is that scripting used anyway?

zERoCooL2479
09-30-2005, 07:24 AM
Yeah, it is part of the ICARUS scripting engine for map events.

razorace
09-30-2005, 12:03 PM
But is it ever used?

ensiform
03-05-2006, 02:39 PM
Client bug: when using graphical hud, armor value doesnt show > 100 even if u have more than 100.


so open up cg_draw.c. we want CG_DrawArmor.

add int realArmor;

add the realArmor line:

armor = ps->stats[STAT_ARMOR];
realArmor = ps->stats[STAT_ARMOR]; // add this line below the above line

below this section:

if (armor> maxArmor)
{
armor = maxArmor;
}

add:

if (realArmor > 200)
{
realArmor = 200;
}

now where its drawing the numfield, change it from armor to realArmor.

CG_DrawNumField (
focusItem->window.rect.x,
focusItem->window.rect.y,
3,
realArmor,
//armor,
focusItem->window.rect.w,
focusItem->window.rect.h,
NUM_FONT_SMALL,
qfalse);

razorace
03-05-2006, 03:55 PM
When do you ever have more than max armor?

ensiform
03-05-2006, 04:48 PM
? if u pick up a large shield you go over 100.

razorace
03-05-2006, 06:03 PM
Oh, ok. See I thought you were supposed to go up to 200 and stop there. Maybe that's just the older Jedi Knight games.

ensiform
03-05-2006, 07:53 PM
well you really dont need to put a limit on it like it did, i put it there just for safety though.

dumbledore
03-06-2006, 12:02 AM
never noticed wrong team colors...

i would like that but siege doesnt work for rs or my basejka :/

im pretty sure by hoth bridge expoit he meant the ctf hoth one, not siege. :/

ensiform
03-06-2006, 03:57 PM
ctf hoth exploit? the ctf bridge exploit is where u can get in the bridge on the team sides. the one where u can stack above ppl and jump into the void above it is on JA+ Official Server only. Slider said it had something to do with the pk3s his provider has.

Kurgan
03-08-2006, 04:08 AM
Oh, ok. See I thought you were supposed to go up to 200 and stop there. Maybe that's just the older Jedi Knight games.

In Jedi Outcast you can have a max of 200 armor (even though the max is 100). If you are at 100 and grab a 100 pickup then it is at 200, but counts down until you're back at 100. In JA, I didn't think that even applied. you could simply never go over 100, at least not in basejka. The most common place you saw this occur in JK2 was on Bespin FFA, as it had two large shield boosts sitting around within easy reach.

JK1 and MotS both had 200 as your shield max IIRC (though MotS Personalities had different maximums, from 175-250). In JA Siege each class has a different shield maximum (ranging from 0-100).

As to the "Hoth bridge exploit" that's commonly being referred to as the one in Siege wherein you can cause an explosion as the objective bridge is being extended, causing it to retract back into the wall and never come out again (this was fixed). The same thing was possible with the "switch based" bridges on Korriban Siege (but you could always re-extend those by force pushing the switch). That other exploit is interesting. You're not supposed to be able to player stack in JA since you slide off their heads, but if you're well coordinated enough you can stay on, and the bots have a real talent for it sometimes... heh

razorace
03-08-2006, 04:53 AM
BTW, I should diserve some real props for fixing both the invisible driver bug AND the locked door det pack bugs in the same week. :)

Kurgan
03-08-2006, 12:51 PM
Locked door detpack bug? I guess I forgot about that one... how did it work?

razorace
03-08-2006, 03:22 PM
Basically, if you're able to physically block a "locked" door (like the team doors on Siege Hoth), it would unlock the door perminately. This was an issue in siege is attacker could be a big advantage by using their enemy's doors.

Kurgan
03-08-2006, 03:40 PM
Ah, I see. So all those people sneaking into Hoth base or past the Rancor gate weren't necessarily relying on "traitors" on the other team to let them through!

Good catch, then!

razorace
03-08-2006, 03:58 PM
Well, I can;t take the credit for the discovery, Enisform found it. I just fixed it. :)

Tinny
03-08-2006, 04:02 PM
Teehee, I would mindtrick people and follow them as they're going through a door.

dumbledore
03-08-2006, 05:16 PM
In Jedi Outcast you can have a max of 200 armor (even though the max is 100). If you are at 100 and grab a 100 pickup then it is at 200, but counts down until you're back at 100. In JA, I didn't think that even applied. you could simply never go over 100, at least not in basejka. The most common place you saw this occur in JK2 was on Bespin FFA, as it had two large shield boosts sitting around within easy reach.

it works in basejka too, only thing is that you have to have less than 100 shields before picking up the large one (ie, 99 shields). its easiest to do this on ffa1 at the top where there's a lsb. to see it though you need to use cg_hudfiles 1 :o

Slider744
03-09-2006, 06:04 AM
i Just fixed a few things one week ago for JA+ mod...
here is my code for the fixes...

invisibility vehicle driver bug FIX
It allows a player riding a vehicle inside (like atst) to become invisible by reconnecting the server while beeing inside.

add this at the end of ClientSpawn(gentity_t *ent) in g_client.c

//MODIFICATION => FIX the basejka bug when players are invisible after having reconnecting when beeing inside a vehicle with hideRider 1

ent->r.svFlags &= ~SVF_NOCLIENT;
ent->s.eFlags &= ~EF_NODRAW;
if ( ent->client )
{
ent->client->ps.eFlags &= ~EF_NODRAW;
}



unlock door exploit FIX
I only fixed it for siege gametype. i don't know if it usefull for other gametype at the moment. Moreover this things seen as a bug in siege mod could be usefull in other gametype for mappers.
we need to prevent a blocked door to be unlock because it might screw up siege objectives

in g_mover.c
change this

/*
================
Blocked_Door
================
*/
void Blocked_Door( gentity_t *ent, gentity_t *other )
{
if ( ent->damage ) {
G_Damage( other, ent, ent, NULL, NULL, ent->damage, 0, MOD_CRUSH );
}
if ( ent->spawnflags & MOVER_CRUSHER ) {
return; // crushers don't reverse
}


// reverse direction
Use_BinaryMover( ent, ent, other );
}


with this


/*
================
MODIFICATION added this special function for blocked door to prevent unlock in siege.
I didn't want tthis fix to work in other gametype than siege at the moment.
Use_BinaryMover_blockedDoor
================
*/
void Use_BinaryMover_blockedDoor( gentity_t *ent, gentity_t *other, gentity_t *activator )
{
if ( !ent->use )
{//I cannot be used anymore, must be a door with a wait of -1 that's opened.
return;
}

// only the master should be used
if ( ent->flags & FL_TEAMSLAVE )
{
Use_BinaryMover_blockedDoor( ent->teammaster, other, activator );
return;
}

if ( ent->flags & FL_INACTIVE )
{
return;
}

if ( ent->spawnflags & MOVER_LOCKED )
{//a locked door
return;
}

G_ActivateBehavior(ent,BSET_USE);

ent->enemy = other;
ent->activator = activator;
if(ent->delay)
{
ent->think = Use_BinaryMover_Go;
ent->nextthink = level.time + ent->delay;
}
else
{
Use_BinaryMover_Go(ent);
}
}

/*
================
Blocked_Door
================
*/
void Blocked_Door( gentity_t *ent, gentity_t *other )
{
if ( ent->damage ) {
G_Damage( other, ent, ent, NULL, NULL, ent->damage, 0, MOD_CRUSH );
}
if ( ent->spawnflags & MOVER_CRUSHER ) {
return; // crushers don't reverse
}

//MODIFICATION FIX only for SIEGE => each time there is something under a door like a player, or det pack ... the door will reopen and will be unlocked
// so player can lame siege gametype where some doors are locked and unlocked only if some objectives are completed.
//Use_BinaryMover will unlock the MOVER entity like a doors or teammaster (for group mover items) ==> it needs to be prevent.

// we need to prevent a blocked door to be unlock but only in siege at the moment. i don't know if it usefull for other gametype at the moment. Moreover this things seen as a bug in siege mod could be usefull in other gametype for mappers.
if(g_gametype.integer == GT_SIEGE){

Use_BinaryMover_blockedDoor( ent, ent, other );
return;

}

// reverse direction
Use_BinaryMover( ent, ent, other );
}




DetPAck exploit FIX
This exploit allows player to kill teamates or destroy siege team objectives that they should protect from the other teams.
To use this exploit you have to fire some det packs and then going to spectator or disconnecting the server while the detpack are not exploding yet...
DOing that all the det pack will explode and kill teamates or team objectives...etc...

in g_combat.c
add in G_Damage
after

if (targ->flags & FL_BBRUSH)
{
if (mod == MOD_DEMP2 ||
mod == MOD_DEMP2_ALT ||
mod == MOD_BRYAR_PISTOL ||
mod == MOD_BRYAR_PISTOL_ALT ||
mod == MOD_MELEE)
{ //these don't damage bbrushes.. ever
if ( mod != MOD_MELEE || !G_HeavyMelee( attacker ) )
{ //let classes with heavy melee ability damage breakable brushes with fists
return;
}
}
}


add this

//MODIFICATION FIX : to prevent detpack to blow teamate members and teamate siege objectives when the attacker is switching to spectator or disconnect the server
if(mod == MOD_DET_PACK_SPLASH && ! (attacker && attacker->inuse && attacker->client && attacker->client->pers.connected == CON_CONNECTED ) )
return;
if(mod == MOD_DET_PACK_SPLASH && attacker && attacker->inuse && attacker->client && attacker->client->pers.connected == CON_CONNECTED
&&
( attacker->client->sess.sessionTeam == TEAM_SPECTATOR
||
( g_gametype.integer == GT_SIEGE && attacker->client->sess.siegeDesiredTeam == TEAM_SPECTATOR)
)
)
return;

razorace
03-09-2006, 02:53 PM
haha, great minds think alike. I fixed several of those errors at well. However, I did them a little differently. As always, all OJP bugfixes are available thru the OJP CVS repository.

invisibility vehicle driver bug FIX
The real source of that problem is the unghostrider function not being called for disconnected players. I just made the unghostrider function always apply since there's no reason not to.

unlock door exploit FIX
I just relocked the doors after the Use_BinaryMover but that works too. :)

I haven't actually fixed the last one yet.

Kurgan
03-09-2006, 03:10 PM
Razor, so that means you've now fixed the AT-ST invisibility exploit? Cool!

Slider744
03-10-2006, 01:32 AM
I just relocked the doors after the Use_BinaryMover but that works too.

yes i did that in my first fix because i saw a similar coding in the basejka code somewhere else...

but i prefered to recoder another function...
i didn't think it was good to unlock the doors then call the function and then reclock the doors...

razorace
03-10-2006, 01:52 AM
Yeah, I was a bit wary about that as well. However, it seems to work fine.

stubert
03-21-2006, 09:50 AM
all of this should really go into one mod =/

razorace
03-21-2006, 10:29 AM
I think OJP has all the fixes suggested so far except for the last siege fix that slider posted.

ensiform
05-07-2006, 02:18 AM
yay more fixes! :

1. Voice Commands show up in the notify box as well as chat box when console is not up?

Easy Client Fix:
(this also escapes the rest of line to white after name)

cg_event.c

look for this line:

char vchatstr[1024];

change the 3 lines below it to look like this:
strcpy(vchatstr, va("<%s^7: %s>", ci->name, descr));
CG_Printf( "*%s\n", vchatstr );
CG_ChatBox_AddString(vchatstr);

2. CTF Messages dont escape to white after player names:

cg_event.c

CG_PrintCTFMessage

Com_sprintf(printMsg, sizeof(printMsg), "%s ", ci->name);

should be:

Com_sprintf(printMsg, sizeof(printMsg), "%s^7 ", ci->name);

and

Com_sprintf(printMsg, sizeof(printMsg), "%s %s", ci->name, psStringEDString);

should be:

Com_sprintf(printMsg, sizeof(printMsg), "%s^7 %s", ci->name, psStringEDString);

3. New security bug out for quake 3 engine, thought it wasnt fixable without engine?

add this somewhere in bg_misc.c:

/*
============
COM_StripExtensionSafe
============
*/
void COM_StripExtensionSafe( const char *in, char *out, int destsize ) {
int length;

Q_strncpyz(out, in, destsize);

length = strlen(out)-1;
while (length > 0 && out[length] != '.')
{
length--;
if (out[length] == '/')
return; // no extension
}
if (length)
out[length] = 0;
}

and declare it somewhere in bg_public.h:

void COM_StripExtensionSafe( const char *in, char *out, int destsize );

now replace all instances of COM_StripExtension in your source with

COM_StripExtensionSafe and the destsize should be sizeof(change me to the char of out)

ie:

COM_StripExtensionSafe(fileptr, configname, sizeof(configname));

even more...

cg_servercmds.c:

look for "remapShader" command in the function CG_ServerCommand and replace that whole if statement for remapShader to:

if ( Q_stricmp (cmd, "remapShader") == 0 )
{
if (trap_Argc() == 4)
{
char shader1[MAX_QPATH];
char shader2[MAX_QPATH];
char shader3[MAX_QPATH];

Q_strncpyz(shader1, CG_Argv(1), sizeof(shader1));
Q_strncpyz(shader2, CG_Argv(2), sizeof(shader2));
Q_strncpyz(shader3, CG_Argv(3), sizeof(shader3));

trap_R_RemapShader(shader1, shader2, shader3);
}

return;
}

last file in cgame for this bugfix, cg_syscalls.c:

comment out or delete the trap_R_RemapShader function and replace it with this:

void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) {
char oldShaderTMP[MAX_QPATH];
char newShaderTMP[MAX_QPATH];
Q_strncpyz(oldShaderTMP, oldShader, sizeof(oldShaderTMP));
Q_strncpyz(newShaderTMP, newShader, sizeof(newShaderTMP));
COM_StripExtensionSafe(oldShaderTMP, oldShaderTMP, sizeof(oldShaderTMP));
COM_StripExtensionSafe(newShaderTMP, newShaderTMP, sizeof(newShaderTMP));
syscall( CG_R_REMAP_SHADER, oldShaderTMP, newShaderTMP, timeOffset );
}

k now open up ui_syscalls.c:

do the same thing for ui_syscalls.c as cg_syscalls.c only the syscall itself is changed ( UI_R_REMAP_SHADER ):

void trap_R_RemapShader( const char *oldShader, const char *newShader, const char *timeOffset ) {
char oldShaderTMP[MAX_QPATH];
char newShaderTMP[MAX_QPATH];
Q_strncpyz(oldShaderTMP, oldShader, sizeof(oldShaderTMP));
Q_strncpyz(newShaderTMP, newShader, sizeof(newShaderTMP));
COM_StripExtensionSafe(oldShaderTMP, oldShaderTMP, sizeof(oldShaderTMP));
COM_StripExtensionSafe(newShaderTMP, newShaderTMP, sizeof(newShaderTMP));
syscall( UI_R_REMAP_SHADER, oldShaderTMP, newShaderTMP, timeOffset );
}

now ui_atoms.c:

look for this line:

if ( Q_stricmp (cmd, "postgame") == 0 ) {

above it add:

if ( Q_stricmp (cmd, "remapShader") == 0 ) {
if (trap_Argc() == 4) {
char shader1[MAX_QPATH];
char shader2[MAX_QPATH];
char shader3[MAX_QPATH];

Q_strncpyz(shader1, UI_Argv(1), sizeof(shader1));
Q_strncpyz(shader2, UI_Argv(2), sizeof(shader2));
Q_strncpyz(shader3, UI_Argv(3), sizeof(shader3));

trap_R_RemapShader(shader1, shader2, shader3);
return qtrue;
}
}

i think that is all for this bug.

Source of bug:

http://www.gamer.nl/doc/32206

Translated by Babelfish:


Gamers which still old, on the Quake 3 Engine play based games have been warned. Are not only slow you desperately, also a serious leak in the engine as a result of which can computer offenders, crackers and other gajes take over your PC'tje, has been discovered.

The leak is caused by "boundary error" during the processing of the "remapShader" commando. This can lead to a buffer overflow, as a result of which the attacker can carry out in question random commandos and software on the vulnerable PC..

If this is all too technical for your, then you must remember simply to make no connection with "malicious servers". An attacker must to abuse the leak, as it happens, by means of its server a command to the PC. send.

Enemy Territory 2.60, return to Castle Wolfenstein 1.41 and Quake III and 1.32b is vulnerable, but also other versions its probable leak.

4. Limbs dont take team color if u have a custom rgb model

g_combat.c:

in function G_Dismember:

limb->s.customRGBA[0] = ent->s.customRGBA[0];
limb->s.customRGBA[1] = ent->s.customRGBA[1];
limb->s.customRGBA[2] = ent->s.customRGBA[2];
limb->s.customRGBA[3] = ent->s.customRGBA[3];

should be:

if (g_gametype.integer >= GT_TEAM) {
switch(ent->client->sess.sessionTeam)
{
case TEAM_RED:
limb->s.customRGBA[0] = 255;
limb->s.customRGBA[1] = 0;
limb->s.customRGBA[2] = 0;
break;
case TEAM_BLUE:
limb->s.customRGBA[0] = 0;
limb->s.customRGBA[1] = 0;
limb->s.customRGBA[2] = 255;
break;
default:
limb->s.customRGBA[0] = ent->s.customRGBA[0];
limb->s.customRGBA[1] = ent->s.customRGBA[1];
limb->s.customRGBA[2] = ent->s.customRGBA[2];
limb->s.customRGBA[3] = ent->s.customRGBA[3];
break;
}
} else {
limb->s.customRGBA[0] = ent->s.customRGBA[0];
limb->s.customRGBA[1] = ent->s.customRGBA[1];
limb->s.customRGBA[2] = ent->s.customRGBA[2];
limb->s.customRGBA[3] = ent->s.customRGBA[3];
}

5. weird tint for the meters and values when playing ffa or on blue team

cg_draw.c

look for this:

if (cgs.gametype >= GT_TEAM && cgs.gametype != GT_SIEGE)
{ // tint the hud items based on team
if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED )
hudTintColor = redhudtint;
else if (cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE )
hudTintColor = bluehudtint;
else // If we're not on a team for whatever reason, leave things as they are.
hudTintColor = colorTable[CT_WHITE];
}
else
{ // tint the hud items white (dont' tint)
hudTintColor = colorTable[CT_WHITE];
}

replace it with:

hudTintColor = redhudtint; // always use red because blue and white screws it up

6. flag icons have a dark tint when you are in scope because raven forgets to do trap_R_SetColor(NULL) after every change in color thats not needed anymore.

cg_draw.c again:

CG_DrawFlagStatus

above the !cg.snap line add:

trap_R_SetColor( NULL );

also CG_DrawPowerupIcons

add

trap_R_SetColor( NULL );

same spot as flagstatus

ensiform
07-30-2006, 09:57 PM
This code in G_SayTo doesn't really work and it is kinda pointless to disable chat, it would make more sense to write something better that disables the voice chat instead.

if (g_gametype.integer == GT_SIEGE &&
ent->client && (ent->client->tempSpectate >= level.time || ent->client->sess.sessionTeam == TEAM_SPECTATOR) &&
other->client->sess.sessionTeam != TEAM_SPECTATOR &&
other->client->tempSpectate < level.time)
{ //siege temp spectators should not communicate to ingame players
return;
}

if you still use the base method of voice_cmd add this in Cmd_VoiceCommand_f:

if ( g_gametype.integer == GT_SIEGE &&
ent->client->tempSpectate >= level.time ) {
return;
}

otherwise add it to G_Voice.

ensiform
07-31-2006, 03:38 PM
don't show the cursor during map load:

ui_main.c in function _UI_Refresh:

look for this piece of code:

// draw cursor
UI_SetColor( NULL );
if (Menu_Count() > 0) {
UI_DrawHandlePic( uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory, 48, 48, uiInfo.uiDC.Assets.cursor);
}

change it to:

// draw cursor
UI_SetColor( NULL );
if (Menu_Count() > 0) {
uiClientState_t cstate;
trap_GetClientState( &cstate );
if(cstate.connState <= CA_DISCONNECTED || cstate.connState >= CA_ACTIVE) {
UI_DrawHandlePic( uiInfo.uiDC.cursorx, uiInfo.uiDC.cursory, 48, 48, uiInfo.uiDC.Assets.cursor);
}
}

ensiform
08-01-2006, 11:40 PM
k so this one is kind of raven's fault, they forgot many many many checks for their EF_DOUBLE_AMMO, thus tech's cannot give you ammo > than the normal max and you cannot pickup ammo > normal max (this is usually for siege).

Open up g_items.c and Change the function "Add_Ammo" to look like this:

void Add_Ammo (gentity_t *ent, int weapon, int count)
{
if ( ent->client->ps.eFlags & EF_DOUBLE_AMMO ) {
if ( ent->client->ps.ammo[weapon] < ammoData[weapon].max*2 )
{
ent->client->ps.ammo[weapon] += count;
if ( ent->client->ps.ammo[weapon] > ammoData[weapon].max*2 )
{
ent->client->ps.ammo[weapon] = ammoData[weapon].max*2;
}
}
} else {
if ( ent->client->ps.ammo[weapon] < ammoData[weapon].max )
{
ent->client->ps.ammo[weapon] += count;
if ( ent->client->ps.ammo[weapon] > ammoData[weapon].max )
{
ent->client->ps.ammo[weapon] = ammoData[weapon].max;
}
}
}
}

Open up g_utils.c and look for the function "G_UseDispenserOn":

change the else if statement for ammodisp to:

else if (dispType == HI_AMMODISP)
{
if (ent->client->medSupplyDebounce < level.time)
{ //do the next increment
//increment based on the amount of ammo used per normal shot.
target->client->ps.ammo[weaponData[target->client->ps.weapon].ammoIndex] += weaponData[target->client->ps.weapon].energyPerShot;

if ( target->client->ps.eFlags & EF_DOUBLE_AMMO ) {
if (target->client->ps.ammo[weaponData[target->client->ps.weapon].ammoIndex] > ammoData[weaponData[target->client->ps.weapon].ammoIndex].max*2)
{ //cap it off
target->client->ps.ammo[weaponData[target->client->ps.weapon].ammoIndex] = ammoData[weaponData[target->client->ps.weapon].ammoIndex].max*2;
}
} else {
if (target->client->ps.ammo[weaponData[target->client->ps.weapon].ammoIndex] > ammoData[weaponData[target->client->ps.weapon].ammoIndex].max)
{ //cap it off
target->client->ps.ammo[weaponData[target->client->ps.weapon].ammoIndex] = ammoData[weaponData[target->client->ps.weapon].ammoIndex].max;
}
}

//base the next supply time on how long the weapon takes to fire. Seems fair enough.
ent->client->medSupplyDebounce = level.time + weaponData[target->client->ps.weapon].fireTime;
}
target->client->isMedSupplied = level.time + 500;
}

next function down "G_CanUseDispOn":


change the else if statement for ammodisp to:

else if (dispType == HI_AMMODISP)
{
if (ent->client->ps.weapon <= WP_NONE || ent->client->ps.weapon > LAST_USEABLE_WEAPON)
{ //not a player-useable weapon
return 0;
}

if ( ent->client->ps.eFlags & EF_DOUBLE_AMMO ) {
if (ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex] < ammoData[weaponData[ent->client->ps.weapon].ammoIndex].max*2)
{ //needs more ammo for current weapon
return 1;
}
} else {
if (ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex] < ammoData[weaponData[ent->client->ps.weapon].ammoIndex].max)
{ //needs more ammo for current weapon
return 1;
}
}

//needs none
return 0;
}

I think that is all.

razorace
08-02-2006, 03:10 PM
This code in G_SayTo doesn't really work and it is kinda pointless to disable chat, it would make more sense to write something better that disables the voice chat instead.
I think the point was to prevent dead/spectators from revealing information to live players in the game.

ensiform
08-02-2006, 05:03 PM
does not matter, its useless code that doesn't work. people can still see your chat. i could have swore Enemy Territory had something like this for voice chats but i cant seem to find it for "Dead" Players.

ensiform
08-03-2006, 05:31 PM
this bug is kinda funny, raven forgot to put a tempSpec check in /follow and followprev/next.

Cmd_Follow_f add this below the other spectator check:

// can't follow another spectator
if ( level.clients[ i ].tempSpectate >= level.time ) {
return;
}

Cmd_FollowCycle_f add this below the other spectator check in the do while loop:

// can't follow another spectator
if ( level.clients[ clientnum ].tempSpectate >= level.time ) {
continue;
}

also, there are some pieces in the code that check if tempSpectate > level.time, it should always be >= level.time because level.time and above means you are tempSpectate, less than means you are not.

razorace
08-03-2006, 07:09 PM
Mind explaining what this fixes?

ensiform
08-03-2006, 08:18 PM
if you are a real spectator (team s) and watching a siege game and someone who is "dead" (tempSpectator) you follow them around while they are in tempSpectate mode. This fixes it so that you don't follow them while they are moving freely.


Btw, I have figured out a way to make spectators move through most entities now even doors, and the security walls on desert and even the red forcefield on hoth. This way spectators do not have to rely on using the crappy teleport thing which doesn't always work if the map has a forced trigger or sometimes it teleports you out of the level causing you to be in the void. :/ If you want me to post this its a bit of a hack in some trace code on server and client for slight stability but it works very well.

ensiform
08-03-2006, 11:59 PM
Also, somewhat of a bug: There should only be 3 places where ps.clientNum should be used on the game module, anything else can potentially cause troubles with specators. For example, your code for auto balance in ojp will sometimes spam to the spectator "You were switched" message. Or even use the spectator's name in messages, or in the duel/pduel check code it could potentially pick a spectator when it didn't mean to.

You should only have these lines:

Near Top of ClientUserInfoChanged:

client->ps.clientNum = clientNum;

Here in ClientSpawn:

client->ps.clientNum = index;
//give default weapons
client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );

and...

In Stop Following:

ent->client->ps.clientNum = ent - g_entities;

Use gentity_t->s.number or gentity_t - g_entities.

(gentity_t being the entity you want the number from)

anything ps->clientNum (meaning something from pmove usually) and client-side is required that it be used but the server no.

ensiform
08-04-2006, 03:13 AM
I don't really want to describe it however its hard to explain why it is a bug:

- Play hoth until you gain the access codes
- Get the codes
- Bring them to AT-ST and get in and type this in console without the quotes but the space:
" /reconnect"
- Even though the player may not be still visible, the objective item never was told the client wasn't 'inuse'. / disconnected.

Add this to ClientDisconnect just below this line:

ent->r.contents = 0;

if (ent->client->holdingObjectiveItem > 0)
{ //carrying a siege objective item - make sure it updates and removes itself from us now in case this is an instant death-respawn situation
gentity_t *objectiveItem = &g_entities[ent->client->holdingObjectiveItem];

if (objectiveItem->inuse && objectiveItem->think)
{
objectiveItem->think(objectiveItem);
}
}

stubert
08-05-2006, 04:07 AM
damn son u findin dem bugs yo

ensiform
08-11-2006, 05:48 PM
Minor bug when pressing fire on a speeder that allows the speeder to still fire if only 1 of two sabers are on or if 1 blade of staff is on.

Open up g_active.c and just above G_CheapWeaponFire function extern this:

extern qboolean BG_SabersOff( playerState_t *ps );

then inside the function the following line should be repleaced:

(rider->client->ps.weapon != WP_SABER || !rider->client->ps.saberHolstered))

with:

(rider->client->ps.weapon != WP_SABER || !BG_SabersOff(&rider->client->ps)))

ensiform
08-13-2006, 02:25 AM
Anybody ever heard of the "speeder lag 'bug'" ?

I think I've got a theory as to why. I was playing siege earlier today and I had originally though, well hell maybe they forgot to clear ent->client->ps.m_iVehicleNum in StopFollowing because somebody had mentioned that it only occurs after spectating somebody.

This code is untested but I'm just wondering if maybe forcing the spectator to exit the vehicle if they were in one.

StopFollowing (g_cmds.c):

if (ent->client->ps.m_iVehicleNum)
{ //tell it I'm getting off
gentity_t *veh = &g_entities[ent->client->ps.m_iVehicleNum];

if (veh->inuse && veh->client && veh->m_pVehicle)
{
veh->m_pVehicle->m_pVehicleInfo->Eject(veh->m_pVehicle, (bgEntity_t *)ent, qtrue);
}
}

note: make sure you put this above where it clears ent->client->ps.m_iVehicleNum.

ensiform
08-14-2006, 03:53 PM
zomg this bug is really annoying: http://www.lucasforums.com/showthread.php?t=168805

Why should arena files not load on the server if bot_enable is off? :smash:

Open up these 2 files: g_bot.c, g_main.c

First make G_LoadArenas not static.

Then extern G_LoadArenas above G_InitGame in g_main.c.

Then change this if statement (G_InitGame):

if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
BotAISetup( restart );
BotAILoadMap( restart );
G_InitBots( restart );
}

to this:

if ( trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
BotAISetup( restart );
BotAILoadMap( restart );
G_InitBots( restart );
} else {
// We still want to load arenas even if bot_enable is off so that
// g_autoMapCycle can work let alone any other code that relies on
// using arena information that normally wouldn't be loaded :Nervous
G_LoadArenas();
}

Vruki Salet
08-14-2006, 05:27 PM
Ensiform, what's the difference between setting vars in cfg files with "set" vs. "seta"?

dumbledore
08-15-2006, 12:30 AM
seta forces the vars to be added to your jampserver.cfg, which isn't really necessary. also, there's this weird thing where using seta lets you set cvar_rom / cvar_cheat vars if you do it on cmd line :S

ensiform
08-18-2006, 11:28 PM
Not sure why this happens but here's how to test it:

Make sure you are in siege.
Have a Rancor grab you so he is in your hands, then change your siegeclass before he kills you. You will die, but he will be in an infinite loop of trying to kill you and it won't let you spawn until you /team s.

ensiform
08-23-2006, 05:54 PM
Max Weapon Charge Time seems it can be overridden by +button2 (Use Held Item) So ie: holding a pistol alt charge for as long as you want...

dumbledore
08-24-2006, 07:18 PM
there seems to be a bug in interpolation on playback of demos recorded from a localhost server (created through the create game ui option). if you watch one of these demos, especially in lower timescale, the motion seems jerky compared to normal remote-server demos. i've found this fix for it:

cg_snapshot.c function CG_ReadNextSnapshot
find the line
// FIXME: why would trap_GetSnapshot return a snapshot with the same server time
and change it to
// FIXME: why would trap_GetSnapshot return a snapshot with the same server time
if ( cg.snap && r && dest->serverTime == cg.snap->serverTime ) {
// because we're playing back demos taken by local servers apparently :O
if ( cg.demoPlayback ) continue;
//continue;
} :twogun:

ensiform
08-25-2006, 01:53 PM
setting forcepowers to something illegal can cause a crash after forcechanged and respawn.

Mod List Wraps strange sometimes. I will post screenshot when I get a chance.

ensiform
08-27-2006, 04:27 PM
npc command uses Com_Printf instead of sending a servercommand of print to the specified client thus when trying for syntax and not being the server while using /npc cmd it will only show up for the server.

ensiform
08-27-2006, 04:36 PM
For whatever reason, Raven chose to use strcpy and strncpy in a lot of places instead of Q_strncpyz. This can cause buffer overflows and overruns. There are only a few places i can think of that should be left. Ususally when the dest is a char *something, however a few can be still length checked only when you don't really know what length should be. Also, bg_vehicleLoad and q_shared really should be left alone.

ensiform
08-31-2006, 09:51 PM
Thanks to hex on this one also.

speed and rage don't work together to give you more speed like it did in jk2.

cause: raven used an if speed else if rage else ragerecovery

fix: revert it back (use a cvar though)

bg_pmove.c "BG_AdjustClientSpeed"

look for and replace this code:

if (ps->fd.forcePowersActive & (1 << FP_SPEED))
{
ps->speed *= 1.7;
}
else if (ps->fd.forcePowersActive & (1 << FP_RAGE))
{
ps->speed *= 1.3;
}
else if (ps->fd.forceRageRecoveryTime > svTime)
{
ps->speed *= 0.75;
}

with:

if (ps->fd.forcePowersActive & (1 << FP_SPEED))
{
ps->speed *= 1.7;
}

if (ps->fd.forcePowersActive & (1 << FP_RAGE))
{
ps->speed *= 1.3;
}
else if (ps->fd.forceRageRecoveryTime > svTime)
{
ps->speed *= 0.75;
}

The client-side will be needed for this for best performance.

ensiform
08-31-2006, 10:09 PM
If you haven't respawned into temp spectator mode and in siege, you can still use your holdable items because they are not cleared :doh:

g_combat.c "player_die"

look for these lines:

// remove powerups
memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) );

below it add:

self->client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
self->client->ps.stats[STAT_HOLDABLE_ITEM] = 0;

=X=Master HeX
09-03-2006, 10:43 AM
setting forcepowers to something illegal can cause a crash after forcechanged and respawn.

This is caused by a malformed forcepower string. To fix this we need to take any garbage the client sends us and turn it into a real forcepower string. A real forcepower string looks like #-#-##################. To do this we try to read anything the client gives us and then we fill in the blanks.

Add the following somewhere:

void FR_NormalizeForcePowers(char *powerOut, int powerLen)
{
char powerBuf[128];
char readBuf[2];
int finalPowers[21] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int i, c;

if (powerLen >= 128 || powerLen < 1)
{ //This should not happen. If it does, this is obviously a bogus string.
//They can have this string. Because I said so.
strcpy(powerBuf, "7-1-032330000000001333");
}
else
{
Q_strncpyz(powerBuf, powerOut, sizeof(powerBuf)); //copy it as the original
}

c = 0;
i = 0;
while (i < powerLen && i < 128 && powerBuf[i] && powerBuf[i] != '\n' && powerBuf != '\0' && c < NUM_FORCE_POWERS+2)
{
if (powerBuf[i] != '-')
{
readBuf[0] = powerBuf[i];
readBuf[1] = 0;
finalPowers[c] = atoi(readBuf);
c++;
}
i++;
}

strcpy(powerOut, va("%i-%i-%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i\0",
finalPowers[0], finalPowers[1], finalPowers[2],
finalPowers[3], finalPowers[4], finalPowers[5],
finalPowers[6], finalPowers[7], finalPowers[8],
finalPowers[9], finalPowers[10], finalPowers[11],
finalPowers[12], finalPowers[13], finalPowers[14],
finalPowers[15], finalPowers[16], finalPowers[17],
finalPowers[18], finalPowers[19], finalPowers[20]));
}

In g_client.c, look for:

Q_strncpyz( forcePowers, Info_ValueForKey (userinfo, "forcepowers"), sizeof( forcePowers ) );

replace with:

if (!(ent->r.svFlags & SVF_BOT))
{ // clients could have a bad forcepower string
char *n;
n = Info_ValueForKey (userinfo, "forcepowers");
FR_NormalizeForcePowers(n, strlen(n));
strcpy( forcePowers, n );
}
else
{
Q_strncpyz( forcePowers, Info_ValueForKey (userinfo, "forcepowers"), sizeof( forcePowers ) );
}


in w_force.c, look for:

Q_strncpyz( forcePowers, Info_ValueForKey (userinfo, "forcepowers"), sizeof( forcePowers ) );

replace with:

if (!(ent->r.svFlags & SVF_BOT))
{
char *s;
s = Info_ValueForKey (userinfo, "forcepowers");
FR_NormalizeForcePowers(s, strlen(s));
strcpy( forcePowers, s );
}
else
{
Q_strncpyz( forcePowers, Info_ValueForKey (userinfo, "forcepowers"), sizeof( forcePowers ) );
}


**EDIT** Fixed to correct bot name issues.

ensiform
09-03-2006, 09:43 PM
This works for that except for when certain bots like the basejka bots do not have a funname defined or if a player is /name "" it will set their name to 0-0-000000000000000000

=X=Master HeX
09-04-2006, 09:55 AM
Hmm, I see.. Interesting side effect.

That may require some modification to how names are checked, and bots names are stored. I will update the fix as soon as I have some time to run tests.

Or, it could be *s being null so the forcepower info is getting placed in there.

=X=Master HeX
09-04-2006, 01:48 PM
I fixed it so it doesn't mess up bot names. I couldn't reproduce the client name being set to "" bug.

ensiform
09-04-2006, 02:01 PM
K, applied the fix. i will test the blank name thing again to see.

Also i would suggest using Q_strncpyz instead of strcpy in these situations:

strcpy(powerBuf, "7-1-032330000000001333"); => Q_strncpyz(powerBuf, "7-1-032330000000001333", sizeof(powerBuf));

strcpy(powerBuf, powerOut); //copy it as the original => Q_strncpyz(powerBuf, powerOut, sizeof(powerBuf)); //copy it as the original

strcpy( forcePowers, n ); => Q_strncpyz( forcePowers, n, sizeof(forcePowers) );

strcpy( forcePowers, s ); => Q_strncpyz( forcePowers, s, sizeof(forcePowers) );

=X=Master HeX
09-04-2006, 03:37 PM
I just overloaded my strcpy functions to use a safe bounds checking version.

=X=Master HeX
09-04-2006, 03:39 PM
Issue with charging weapons max charge time not being checked if you hold the use item key. Working on a stable fix.

razorace
09-04-2006, 10:02 PM
Here's a really old bug fix that CerburuS told me about ages ago. However, I never actually tried it so your mileage may vary. I'll give this a shot when I have time as well.

just wanted to let you know that there's a bug in basejka which disables Areaportals in Siege permanently if that Areaportal is "open" when the round is being restarted (i.e. the Areaportal will not work at all in the next round, causing bad FPS for the players).

In basejka Siege it's obviously not as much of an issue as it is in MBII since there's only one round restart per map at max (opposed to 20+ in MBII). Thought you might be interested nonetheless...

I've written a relatively simple fix for this problems - could forward to you if you want. It'd need a small modification for basejka though since I'm using a MBII-specific value to determine whether the server is currently in round transition.

Well, area portals should be used quite frequently in Siege maps - I imagine that siege_desert for example uses quite a few. The only one I know for sure is located at the Command Center doors at siege_hoth. They should be easy to detect via /r_showtris 1 though.

//fix for self-deactivating areaportals in Siege
if ( ent->s.eType == ET_MOVER && g_gametype.integer == GT_SIEGE && level.MBIntermission)
{
if ( !Q_stricmp("func_door", ent->classname) && ent->moverState != MOVER_POS1 )
{
SetMoverState( ent, MOVER_POS1, level.time );
if ( ent->teammaster == ent || !ent->teammaster )
{
trap_AdjustAreaPortalState( ent, qfalse );
}

//stop the looping sound
ent->s.loopSound = 0;
ent->s.loopIsSoundset = qfalse; }
continue;
}
This is the code I inserted in g_main.c (around line 4169 in the MB version). I'd just send you the file but since my comp just got fried 2 hours ago, this would take a while ;) Basically you should be able to insert it anywhere in the G_RunFrame function.

Basically it closes any door that is not closed (i.e. opening, closing, or open) and activates the areaportal again. According to our beta testers, it works pretty well so far. If you can think of a better way to do this though, just let me know :)

The only thing that will need adapting is the level.MBintermission value I'm using since that one's MB specific.

Hope it's at least of some help.

EDIT: Ensiform has come up with code to fix this bug for basejka. Look down in this thread for it.

=X=Master HeX
09-06-2006, 01:37 AM
There is a bug with saber blocking not working at all if you die while your saber is knocked out of your hands. I am currently working on a stable fix.

ensiform
09-06-2006, 03:26 PM
For those not able to understand how the areaportal fix works with non-MB2 code open up g_main.c and look for the following piece of code:

if ( ent->s.eType == ET_MOVER ) {
G_RunMover( ent );
continue;
}

below it add:

//fix for self-deactivating areaportals in Siege
if ( ent->s.eType == ET_MOVER && g_gametype.integer == GT_SIEGE && level.intermissiontime)
{
if ( !Q_stricmp("func_door", ent->classname) && ent->moverState != MOVER_POS1 )
{
SetMoverState( ent, MOVER_POS1, level.time );
if ( ent->teammaster == ent || !ent->teammaster )
{
trap_AdjustAreaPortalState( ent, qfalse );
}

//stop the looping sound
ent->s.loopSound = 0;
ent->s.loopIsSoundset = qfalse;
}
continue;
}

=X=Master HeX
09-07-2006, 10:51 AM
The following appears to fix the bug with saber collision after you die when your saber was knocked away.

In g_combat.c, function player_die
Find:

self->client->ps.saberEntityNum = self->client->saberStoredIndex; //in case we died while our saber was knocked away.

After, add:

if (self->client->ps.weapon == WP_SABER && self->client->saberKnockedTime)
{
gentity_t *saberEnt = &g_entities[self->client->ps.saberEntityNum];
//G_Printf("DEBUG: Running saber cleanup for %s\n", self->client->pers.netname);
self->client->saberKnockedTime = 0;
saberReactivate(saberEnt, self);
saberEnt->r.contents = CONTENTS_LIGHTSABER;
saberEnt->think = saberBackToOwner;
saberEnt->nextthink = level.time;
G_RunObject(saberEnt);
}

ensiform
09-07-2006, 04:01 PM
nice find ;)

btw, don't forget to extern saberReactivate and saberBackToOwner above player_die.

stubert
09-07-2006, 04:41 PM
i'd say leave the changes that raven made to speed + rage intact... it would way unbalance darkside =S

ensiform
09-07-2006, 05:15 PM
well if someone chooses to add it they can make a toggle then like hex and i have. :nervou

razorace
09-07-2006, 06:26 PM
Wrong thread?

ensiform
09-07-2006, 08:00 PM
no, a toggle for jk2 style speed+rage stacking or jka not allow stacking.

note: the weapon charge thing with use holdable doesnt seem to be fixed yet so ignore that for right now.

razorace
09-07-2006, 09:57 PM
I suggest you delete the post or update it so people will know if reading from the start of the thread.

ensiform
09-08-2006, 08:38 PM
One really should not be able to return their flag from inside a vehicle since you cannot capture or pickup the enemy flag :sithm:

=X=Master HeX
09-09-2006, 01:17 PM
Fixed the bug with using alt fire and then hitting use holdable to hold your charge.

in bg_pmove.c in the function PmoveSingle:

after:

pm = pmove;

add:

if (pm->cmd.buttons & BUTTON_ATTACK && pm->cmd.buttons & BUTTON_USE_HOLDABLE)
{ pm->cmd.buttons &= ~BUTTON_ATTACK;
pm->cmd.buttons &= ~BUTTON_USE_HOLDABLE;}
if (pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->cmd.buttons & BUTTON_USE_HOLDABLE)
{ pm->cmd.buttons &= ~BUTTON_ALT_ATTACK;
pm->cmd.buttons &= ~BUTTON_USE_HOLDABLE;}

razorace
09-12-2006, 07:32 PM
I'm in the process of intergrating all the bugfixes into OJP and I notice that I can't replicate the following bug. Ensiform, if there maybe additional conditions for this bug to occur? Thanks!
2. bot_minplayers removerandom bot bug where it kicks spectators watching them instead of the bot:

change the following in g_bot.c:

trap_SendConsoleCommand( EXEC_INSERT, va("kick \"%s\"\n", netname) );

to

trap_SendConsoleCommand( EXEC_INSERT, va("clientkick \"%d\"\n", cl->ps.clientNum));

Edit: Also, Ensiform, would you mind adding additional position information for those bugs mentioned in the starting post? I'd feel more comfortable if I could see exactly where the code is supposed to go. Maybe just show the line of code that preceeds the patched code? Thanks!

razorace
09-12-2006, 11:09 PM
Also, the bug fix for item 1 in Enisform's first post doesn't fully work. It looks like currentOrigin doesn't work for spectators so you gotta do a spectator check and then return values based on that. As such, the proper fix should be....

g_cmd.c
void Cmd_Where_f( gentity_t *ent ) {
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
}

to

g_cmd.c

void Cmd_Where_f( gentity_t *ent ) {
if(ent->client && ent->client->sess.sessionTeam != TEAM_SPECTATOR )
{//active players use currentOrigin
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->r.currentOrigin ) ) );
}
else
{
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
}
}

razorace
09-13-2006, 04:46 AM
Alternate fix for a dead body's color didn't match the player's color in team games. The fix offered by Enisform (http://www.lucasforums.com/showpost.php?p=1698122&postcount=12) doesn't conform to the way team colors are normally forced on the player. In addition, I think that Ensiform's method might cause problems with dead body colors in siege.

g_client.c:

SetupGameGhoul2Model()
Change
if ( g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_SIEGE && !g_trueJedi.integer )
{
BG_ValidateSkinForTeam( truncModelName, skin, ent->client->sess.sessionTeam, NULL );
}to
if ( g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_SIEGE && !g_trueJedi.integer )
{
//Also adjust customRGBA for team colors.
float colorOverride[3];

colorOverride[0] = colorOverride[1] = colorOverride[2] = 0.0f;

BG_ValidateSkinForTeam( truncModelName, skin, ent->client->sess.sessionTeam, colorOverride);
if (colorOverride[0] != 0.0f ||
colorOverride[1] != 0.0f ||
colorOverride[2] != 0.0f)
{
ent->client->ps.customRGBA[0] = colorOverride[0]*255.0f;
ent->client->ps.customRGBA[1] = colorOverride[1]*255.0f;
ent->client->ps.customRGBA[2] = colorOverride[2]*255.0f;
}
}

ClientUserinfoChanged()
After
client->ps.customRGBA[3]=255;Addif ( g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_SIEGE && !g_trueJedi.integer )
{
char skin[MAX_QPATH];
float colorOverride[3];

colorOverride[0] = colorOverride[1] = colorOverride[2] = 0.0f;

BG_ValidateSkinForTeam( model, skin, client->sess.sessionTeam, colorOverride);
if (colorOverride[0] != 0.0f ||
colorOverride[1] != 0.0f ||
colorOverride[2] != 0.0f)
{
client->ps.customRGBA[0] = colorOverride[0]*255.0f;
client->ps.customRGBA[1] = colorOverride[1]*255.0f;
client->ps.customRGBA[2] = colorOverride[2]*255.0f;
}
}

ClientSpawn()
After
client->ps.customRGBA[3]=255;Add//update our customRGBA for team colors.
if ( g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_SIEGE && !g_trueJedi.integer )
{
char skin[MAX_QPATH];
char model[MAX_QPATH];
float colorOverride[3];

colorOverride[0] = colorOverride[1] = colorOverride[2] = 0.0f;
Q_strncpyz( model, Info_ValueForKey (userinfo, "model"), sizeof( model ) );

BG_ValidateSkinForTeam( model, skin, savedSess.sessionTeam, colorOverride);
if (colorOverride[0] != 0.0f ||
colorOverride[1] != 0.0f ||
colorOverride[2] != 0.0f)
{
client->ps.customRGBA[0] = colorOverride[0]*255.0f;
client->ps.customRGBA[1] = colorOverride[1]*255.0f;
client->ps.customRGBA[2] = colorOverride[2]*255.0f;
}
}

Edit: Fixed typos in first and second code sections.

ensiform
09-13-2006, 03:00 PM
Also, the bug fix for item 1 in Enisform's first post doesn't fully work. It looks like currentOrigin doesn't work for spectators so you gotta do a spectator check and then return values based on that. As such, the proper fix should be....

g_cmd.c
void Cmd_Where_f( gentity_t *ent ) {
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
}

to

g_cmd.c

void Cmd_Where_f( gentity_t *ent ) {
if(ent->client && ent->client->sess.sessionTeam != TEAM_SPECTATOR )
{//active players use currentOrigin
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->r.currentOrigin ) ) );
}
else
{
trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
}
}


fix? spectators don't really have an origin. try playing a map with location entities in them, it doesn't update and is not supposed to.

ensiform
09-13-2006, 03:02 PM
uhm... put some bots in the game from bot_minplayers, spectate them so that removerandombot will kick them however it will (try) to kick you. if you are localhost it will just say 'cannot kick localhost', also don't use ps.clientnum try something more like:

int idnum;

replace the top of the for loop with this:

for( i = 0; i < level.numConnectedClients; i++ ) {
idnum = level.sortedClients[i];

then use idnum instead of cl->ps.clientNum.

then just get rid of the netname part in G_RemoveRandomBot.

this is what my function looks like:

/*
===============
G_RemoveRandomBot
===============
*/
int G_RemoveRandomBot( int team ) {
int i,idnum;
gentity_t *cl_ent;

for ( i=0 ; i< g_maxclients.integer ; i++ ) {
idnum = level.sortedSlots[i];
cl_ent = g_entities + idnum;
if ( cl_ent->client->pers.connected != CON_CONNECTED ) {
continue;
}
if ( !(cl_ent->r.svFlags & SVF_BOT) ) {
continue;
}
if ( cl_ent->client->ps.powerups[PW_BLUEFLAG] ) {
continue;
}
if ( cl_ent->client->ps.powerups[PW_REDFLAG] ) {
continue;
}
if ( cl_ent->client->ps.powerups[PW_NEUTRALFLAG] ) {
continue;
}
//
if ( cl_ent->client->sess.sessionTeam == TEAM_SPECTATOR
&& cl_ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
{//this entity is actually following another entity so the ps data is for a
//different entity. Bots never spectate like this so, skip this player.
continue;
}
//
if (g_gametype.integer == GT_SIEGE)
{
if ( team >= 0 && cl_ent->client->sess.siegeDesiredTeam != team ) {
continue;
}
}
else
{
if ( team >= 0 && cl_ent->client->sess.sessionTeam != team ) {
continue;
}
}

trap_SendConsoleCommand( EXEC_INSERT, va2("clientkick \"%d\"\n", idnum));
return qtrue;
}
return qfalse;
}

sortedSlots is basically just sortedClients except it sorts by clientnumber instead of score, and va2 is just va but improved/tweaked. my func also skips removing bots that carry flags.

razorace
09-13-2006, 03:45 PM
fix? spectators don't really have an origin. try playing a map with location entities in them, it doesn't update and is not supposed to.
The original intent of the "where" command appears to have been for using spectator mode to determine positions on the map. That's why it doesn't work for active players. But on the flip side, currentOrigin isn't updated for spectators!

ensiform
09-13-2006, 04:24 PM
/viewpos on the client.

razorace
09-13-2006, 05:36 PM
So, "where" is the server side equivilient. I suppose they're a little different due to the view position not being the same as the model origin.

razorace
09-13-2006, 05:39 PM
Say, for the "remapShader" exploit fix, why isn't the third argument protected as well? Is it not possible to overflow that one?

ensiform
09-13-2006, 08:22 PM
? hmm you mean in the trap call? well technically, i just looked at re.RemapShader in the engine and only the first parameter really needs it and the last object is really just a float as far as i can see.

See (this is the fixed version in the engine using a fail-safe checker for COM_StripExtension):

tr_shader.c:

void R_RemapShader(const char *shaderName, const char *newShaderName, const char *timeOffset) {
char strippedName[MAX_QPATH];
int hash;
shader_t *sh, *sh2;
qhandle_t h;

sh = R_FindShaderByName( shaderName );
if (sh == NULL || sh == tr.defaultShader) {
h = RE_RegisterShaderLightMap(shaderName, 0);
sh = R_GetShaderByHandle(h);
}
if (sh == NULL || sh == tr.defaultShader) {
ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: shader %s not found\n", shaderName );
return;
}

sh2 = R_FindShaderByName( newShaderName );
if (sh2 == NULL || sh2 == tr.defaultShader) {
h = RE_RegisterShaderLightMap(newShaderName, 0);
sh2 = R_GetShaderByHandle(h);
}

if (sh2 == NULL || sh2 == tr.defaultShader) {
ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: new shader %s not found\n", newShaderName );
return;
}

// remap all the shaders with the given name
// even tho they might have different lightmaps
COM_StripExtension(shaderName, strippedName, sizeof(strippedName));
hash = generateHashValue(strippedName, FILE_HASH_SIZE);
for (sh = hashTable[hash]; sh; sh = sh->next) {
if (Q_stricmp(sh->name, strippedName) == 0) {
if (sh != sh2) {
sh->remappedShader = sh2;
} else {
sh->remappedShader = NULL;
}
}
}
if (timeOffset) {
sh2->timeOffset = atof(timeOffset);
}
}

old version that exists in jka:

void R_RemapShader(const char *shaderName, const char *newShaderName, const char *timeOffset) {
char strippedName[MAX_QPATH];
int hash;
shader_t *sh, *sh2;
qhandle_t h;

sh = R_FindShaderByName( shaderName );
if (sh == NULL || sh == tr.defaultShader) {
h = RE_RegisterShaderLightMap(shaderName, 0);
sh = R_GetShaderByHandle(h);
}
if (sh == NULL || sh == tr.defaultShader) {
ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: shader %s not found\n", shaderName );
return;
}

sh2 = R_FindShaderByName( newShaderName );
if (sh2 == NULL || sh2 == tr.defaultShader) {
h = RE_RegisterShaderLightMap(newShaderName, 0);
sh2 = R_GetShaderByHandle(h);
}

if (sh2 == NULL || sh2 == tr.defaultShader) {
ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: new shader %s not found\n", newShaderName );
return;
}

// remap all the shaders with the given name
// even tho they might have different lightmaps
COM_StripExtension(shaderName, strippedName);
hash = generateHashValue(shaderName, FILE_HASH_SIZE);
for (sh = hashTable[hash]; sh; sh = sh->next) {
if (Q_stricmp(sh->name, strippedName) == 0) {
if (sh != sh2) {
sh->remappedShader = sh2;
} else {
sh->remappedShader = NULL;
}
}
}
if (timeOffset) {
sh2->timeOffset = atof(timeOffset);
}
}

note how in the fixed version com_stripextension takes into affect the newsize, and the jka one does not.

/*
============
COM_StripExtension
============
*/
void COM_StripExtension( const char *in, char *out ) {
while ( *in && *in != '.' ) {
*out++ = *in++;
}
*out = 0;
}

vs.

/*
============
COM_StripExtensionSafe
============
*/
void COM_StripExtensionSafe( const char *in, char *out, int destsize ) {
int length;

Q_strncpyz(out, in, destsize);

length = strlen(out)-1;
while (length > 0 && out[length] != '.')
{
length--;
if (out[length] == '/')
return; // no extension
}
if (length)
out[length] = 0;
}

i renamed it because i do not like to modify anything in the q_* files

razorace
09-17-2006, 05:50 PM
There's a few typo bugs in the saberMoveData table that result in the wrong animations being played during bottom left/right broken parries, parries, and knockaways.

bg_saber.c, saberMoveData[]
Replace

// Broken parries
{"BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP,
{"BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR,
{"BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL,
{"BParry LR", BOTH_H1_S1_BL, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR,
{"BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL
{"BParry LL", BOTH_H1_S1_BR, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL

// Knockaways
{"Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP,
{"Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR,
{"Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL,
{"Knock LR", BOTH_K1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR,
{"Knock LL", BOTH_K1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL

// Parry
{"Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP,
{"Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR,
{"Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL,
{"Parry LR", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR,
{"Parry LL", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL

with
// Broken parries
{"BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP,
{"BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR,
{"BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL,
{"BParry LR", BOTH_H1_S1_BR, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR,
{"BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR
{"BParry LL", BOTH_H1_S1_BL, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL

// Knockaways
{"Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP,
{"Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR,
{"Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL,
{"Knock LR", BOTH_K1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR,
{"Knock LL", BOTH_K1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL

// Parry
{"Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP,
{"Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR,
{"Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL,
{"Parry LR", BOTH_P1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR,
{"Parry LL", BOTH_P1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL

ensiform
09-20-2006, 04:12 PM
Replacement for DeathmatchScoreboardMessage that comes from Enemy Territory with some enhancements and such so that it will not go over 1022 limit.

Note: if you use unlagged change the ping part to use pers.realPing instead of ps.ping.

// G_SendScore_Add
//
// Add score with clientNum at index i of level.sortedClients[]
// to the string buf.
//
// returns qtrue if the score was appended to buf, qfalse otherwise.
qboolean G_SendScore_Add(gentity_t *ent, int i, char *buf, int bufsize)
{
gclient_t *cl;
int ping, scoreFlags=0, accuracy, perfect;
char entry[256];

entry[0] = '\0';

cl = &level.clients[level.sortedClients[i]];

if ( cl->pers.connected == CON_CONNECTING ) {
ping = -1;
} else {
ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
}

if( cl->accuracy_shots ) {
accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots;
} else {
accuracy = 0;
}
perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0;

Com_sprintf (entry, sizeof(entry),
" %i %i %i %i %i %i %i %i %i %i %i %i %i %i ",
level.sortedClients[i],
cl->ps.persistant[PERS_SCORE],
ping,
(level.time - cl->pers.enterTime)/60000,
scoreFlags,
g_entities[level.sortedClients[i]].s.powerups,
accuracy,
cl->ps.persistant[PERS_IMPRESSIVE_COUNT],
cl->ps.persistant[PERS_EXCELLENT_COUNT],
cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT],
cl->ps.persistant[PERS_DEFEND_COUNT],
cl->ps.persistant[PERS_ASSIST_COUNT],
perfect,
cl->ps.persistant[PERS_CAPTURES]);

if((strlen(buf) + strlen(entry) + 1) > bufsize) {
return qfalse;
}
Q_strcat(buf, bufsize, entry);
return qtrue;
}

/*
==================
G_SendScore

==================
*/
void G_SendScore( gentity_t *ent ) {
int i;
int numSorted;
int count;
// tjw: commands over 1022 will crash the client so they're
// pruned in trap_SendServerCommand()
// 1022 -32 for the startbuffer
char buffer[990];
char startbuffer[32];

numSorted = level.numConnectedClients;

if (numSorted > MAX_CLIENTS)
{
numSorted = MAX_CLIENTS;
}

count = 0;
*buffer = '\0';
*startbuffer = '\0';

Q_strncpyz(startbuffer, va(
"scores %i %i %i",
level.numConnectedClients,
level.teamScores[TEAM_RED],
level.teamScores[TEAM_BLUE]),
sizeof(startbuffer));

// tjw: keep adding scores to the scores command until we fill
// up the buffer.
for(i=0 ; i < numSorted ; i++) {
// tjw: the old version of SendScore() did this. I removed it
// originally because it seemed like an unneccessary hack.
// perhaps it is necessary for compat with CG_Argv()?
if(!G_SendScore_Add(ent, i, buffer, sizeof(buffer))) {
break;
}
count++;
}
if(!count) {
return;
}
trap_SendServerCommand(ent-g_entities, va(
"%s%s", startbuffer, buffer));
}

Now just replace all instances of DeathmatchScoreboardMessage with G_SendScore.

Client-Side fix to allow actually up to MAX_CLIENTS instead of only 20 clients to draw on scoreboard:

cg_servercmds.c:

/*
=================
CG_ParseScores

=================
*/
static void CG_ParseScores( void ) {
int i, powerups;

cg.numScores = atoi( CG_Argv( 1 ) );
if ( cg.numScores > MAX_CLIENTS ) {
cg.numScores = MAX_CLIENTS;
}

cg.teamScores[0] = atoi( CG_Argv( 2 ) );
cg.teamScores[1] = atoi( CG_Argv( 3 ) );

memset( cg.scores, 0, sizeof( cg.scores ) );
for ( i = 0 ; i < cg.numScores ; i++ ) {
//
cg.scores[i].client = atoi( CG_Argv( i * 14 + 4 ) );
cg.scores[i].score = atoi( CG_Argv( i * 14 + 5 ) );
cg.scores[i].ping = atoi( CG_Argv( i * 14 + 6 ) );
cg.scores[i].time = atoi( CG_Argv( i * 14 + 7 ) );
cg.scores[i].scoreFlags = atoi( CG_Argv( i * 14 + 8 ) );
powerups = atoi( CG_Argv( i * 14 + 9 ) );
cg.scores[i].accuracy = atoi(CG_Argv(i * 14 + 10));
cg.scores[i].impressiveCount = atoi(CG_Argv(i * 14 + 11));
cg.scores[i].excellentCount = atoi(CG_Argv(i * 14 + 12));
cg.scores[i].guantletCount = atoi(CG_Argv(i * 14 + 13));
cg.scores[i].defendCount = atoi(CG_Argv(i * 14 + 14));
cg.scores[i].assistCount = atoi(CG_Argv(i * 14 + 15));
cg.scores[i].perfect = atoi(CG_Argv(i * 14 + 16));
cg.scores[i].captures = atoi(CG_Argv(i * 14 + 17));

if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS ) {
cg.scores[i].client = 0;
}
cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score;
cgs.clientinfo[ cg.scores[i].client ].powerups = powerups;

cg.scores[i].team = cgs.clientinfo[cg.scores[i].client].team;
}
}

ensiform
09-20-2006, 04:36 PM
Now for an updated TeamplayInfoMessage.

This is going to be slightly longer because we need to add a new sorted client list to level_locals

add this to level_locals_t struct (g_local.h):

int sortedSlots[MAX_CLIENTS]; // sorted by clientnum

now above CalculateRanks add this function (g_main.c):

/*
=============
SortSlots

=============
*/
int QDECL SortSlots( const void *a, const void *b ) {
return *(int *)a - *(int *)b;
}

Okay now in CalculateRanks,

below this line:

level.sortedClients[level.numConnectedClients] = i;

add:

level.sortedSlots[level.numConnectedClients] = i;

now look for this line:

qsort( level.sortedClients, level.numConnectedClients,
sizeof(level.sortedClients[0]), SortRanks );

below it add:

qsort( level.sortedSlots, level.numConnectedClients,
sizeof(level.sortedSlots[0]), SortSlots );

now g_team.c

A replacement for TeamplayInfoMessage.

// G_SendTeamInfo_Add
//
// Add teaminfo with clientNum at index i of level.sortedSlots[]
// to the string buf.
//
// returns qtrue if the info was appended to buf, qfalse otherwise.
qboolean G_SendTeamInfo_Add(gentity_t *ent, gentity_t *player, int idnum, char *buf, int bufsize)
{
int h, a;
char entry[128];

entry[0] = '\0';

h = player->client->ps.stats[STAT_HEALTH];
a = player->client->ps.stats[STAT_ARMOR];
if (h < 0) h = 0;
if (a < 0) a = 0;

Com_sprintf (entry, sizeof(entry),
" %i %i %i %i %i %i",
idnum, player->client->pers.teamState.location, h, a,
player->client->ps.weapon, player->s.powerups);
if((strlen(buf) + strlen(entry) + 1) > bufsize) {
return qfalse;
}
Q_strcat(buf, bufsize, entry);
return qtrue;
}

/*
==================
G_SendTeamInfo

==================
*/
void G_SendTeamInfo( gentity_t *ent ) {
int i, idnum;
int numSorted;
int count;
gentity_t *player = NULL;
// tjw: commands over 1022 will crash the client so they're
// pruned in trap_SendServerCommand()
// 1022 -32 for the startbuffer
char buffer[990];
char startbuffer[32];

if ( !ent->client->pers.teamInfo )
return;

numSorted = level.numConnectedClients;

if (numSorted > MAX_CLIENTS)
{
numSorted = MAX_CLIENTS;
}

count = 0;
*buffer = '\0';
*startbuffer = '\0';

// tjw: keep adding teaminfos to the tinfo command until we fill
// up the buffer.
for(i=0 ; i < numSorted && count < TEAM_MAXOVERLAY; i++) {
// tjw: the old version of SendScore() did this. I removed it
// originally because it seemed like an unneccessary hack.
// perhaps it is necessary for compat with CG_Argv()?
idnum = level.sortedSlots[i];
player = g_entities + idnum;
if ( !player->inuse || player->client->sess.sessionTeam != ent->client->sess.sessionTeam )
continue;
if(!G_SendTeamInfo_Add(ent, player, idnum, buffer, sizeof(buffer))) {
break;
}
count++;
}
if(!count) {
return;
}
Q_strncpyz(startbuffer, va(
"tinfo %i",
count),
sizeof(startbuffer));

trap_SendServerCommand(ent-g_entities, va(
"%s%s", startbuffer, buffer));
}

Now just replace any instances of TeamplayInfoMessage with G_SendTeamInfo.

ensiform
09-20-2006, 05:55 PM
Okay, here is a good vsnprintf that works (maybe not for mac but you will have to check) for jka since you dont use qvm:

q_shared.c (somewhere above Com_sprintf preferably):

(comes from idStr::vsnPrintf from Str.c and Str.h from D3/Q4 1.3 SDK)

/*
============
Q_vsnprintf

vsnprintf portability:

C99 standard: vsnprintf returns the number of characters (excluding the trailing
'\0') which would have been written to the final string if enough space had been available
snprintf and vsnprintf do not write more than size bytes (including the trailing '\0')

win32: _vsnprintf returns the number of characters written, not including the terminating null character,
or a negative value if an output error occurs. If the number of characters to write exceeds count, then count
characters are written and -1 is returned and no trailing '\0' is added.

Q_vsnprintf: always appends a trailing '\0', returns number of characters written (not including terminal \0)
or returns -1 on failure or if the buffer would be overflowed.
============
*/
int Q_vsnprintf( char *dest, int size, const char *fmt, va_list argptr ) {
int ret;

#ifdef _WIN32
ret = _vsnprintf( dest, size-1, fmt, argptr );
#else
ret = vsnprintf( dest, size, fmt, argptr );
#endif
dest[size-1] = '\0';
if ( ret < 0 || ret >= size ) {
return -1;
}
return ret;
}

q_shared.h

look for this code:

// strlen that discounts Quake color sequences
int Q_PrintStrlen( const char *string );
// removes color sequences from string
char *Q_CleanStr( char *string );

add this below it:

int Q_vsnprintf( char *dest, int size, const char *fmt, va_list argptr );

Use this for many things like, G_Printf, G_LogPrintf, G_Error, CG_Printf, and CG_Error that use vsprintf. However, leave va, and if you wish to use a cleaner Com_sprintf try something like this:

(comes from Enemy Territory)

void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
int ret;
va_list argptr;

va_start (argptr,fmt);
ret = Q_vsnprintf (dest, size, fmt, argptr);
va_end (argptr);
if (ret == -1) {
Com_Printf ("Com_sprintf: overflow of %i bytes buffer\n", size);
}
}

My updated G_LogPrintf with the time fix also:

/*
=================
G_LogPrintf

Print to the logfile with a time stamp if it is open
=================
*/
void QDECL G_LogPrintf( const char *fmt, ... ) {
va_list argptr;
char string[1024];
int mins, seconds, msec, l;

msec = level.time;

seconds = msec / 1000;
mins = seconds / 60;
seconds %= 60;
msec %= 1000;

Com_sprintf( string, sizeof(string), "%i:%02i ", mins, seconds );

l = strlen( string );

va_start( argptr, fmt );
Q_vsnprintf( string + l, sizeof( string ) - l, fmt, argptr );
va_end( argptr );

if ( g_dedicated.integer ) {
G_Printf( "%s", string + l );
}

if ( !level.logFile ) {
return;
}

trap_FS_Write( string, strlen( string ), level.logFile );
}

ensiform
09-30-2006, 03:11 PM
possibly giving ammo to incorrect index in cg_predict.c.

Scroll to bottom of CG_TouchItem in cg_predict.c

replace these lines:

if ( !cg.predictedPlayerState.ammo[ item->giTag ] ) {
cg.predictedPlayerState.ammo[ item->giTag ] = 1;
with:

if ( !cg.predictedPlayerState.ammo[ weaponData[item->giTag].ammoIndex ] ) {
cg.predictedPlayerState.ammo[ weaponData[item->giTag].ammoIndex ] = 1;

ensiform
12-02-2006, 12:48 AM
I've just found out that Slider's fix posted on a previous page in this thread about detpacks and siege objectives when a player changes teams/disconnects does not entirely work. I've tested it and it still seems to cause them to damage the objectives.

Better solution: Remove all detpacks, trip mines, and thermals from the player who switches teams or disconnects/reconnects.

Also: BugFix13 is only part of the problem with the siege bridge lame, a properly placed player or other objects can still block the bridge and make it go back in. Fix: in SP_func_door make ent->spawnflags be 5 (start_open and crusher) for that mover and set ent->damage to 9999 if it is on mp/siege_hoth and is func_door and has an ent->target of "droptheclip".

razorace
12-02-2006, 03:10 PM
That's strange, I thought my fix made it so that the mover just instantly kills anything blocking it if it gets jammed...

ensiform
12-02-2006, 04:02 PM
Well I just looked and that specific mover itself is not set to be a crusher or do damage so it really wouldn't do much to players.

razorace
12-02-2006, 07:36 PM
Point taken. Go ahead and impliment it for OJP if you want. :)

ensiform
12-02-2006, 08:11 PM
Will do :)

I may implement my other fix for the detpacks/trips/thermals and a person changing teams or leaving server if that is alright with you.

razorace
12-02-2006, 09:42 PM
Sure, go ahead. You don't need pre-approval for minor bugfixes. I just mainly want to know about things that majorly rewrite gameplay/code beforehand. :)

zERoCooL2479
12-22-2006, 03:46 PM
In ai_main.c, replace


if (InFieldOfVision(bs->viewangles, 100, e_ang_vec))
{ //Our enemy has his saber holstered and has challenged us to a duel, so challenge him back
if (!bs->cur_ps.saberHolstered)
{
Cmd_ToggleSaber_f(&g_entities[bs->client]);
}
else
{
if (bs->currentEnemy->client->ps.duelIndex == bs->client &&
bs->currentEnemy->client->ps.duelTime > level.time &&
!bs->cur_ps.duelInProgress)
{
Cmd_EngageDuel_f(&g_entities[bs->client]);
}
}


with:


if (InFieldOfVision(bs->viewangles, 100, e_ang_vec) && !bs->cur_ps.duelInProgress &&
!bs->currentEnemy->client->ps.fd.forcePowersActive )
{ //Our enemy has his saber holstered and has challenged us to a duel, so challenge him back
if (!bs->cur_ps.saberHolstered && !bs->cur_ps.saberInFlight )
{
Cmd_ToggleSaber_f(&g_entities[bs->client]);
}
else
{
if (bs->currentEnemy->client->ps.duelIndex == bs->client &&
bs->currentEnemy->client->ps.duelTime > level.time &&
!bs->cur_ps.duelInProgress)
{
Cmd_EngageDuel_f(&g_entities[bs->client]);
}
}


This will prevent people from laming the bots when they have their guard down.

Soh Raun
01-07-2007, 12:12 PM
This is my first post here, but I've been reading this forum for a while and this thread was very useful to me while programming my mod, so it's time for me to contribute too :)

I have finally fixed the "speeder lag" bug ensiform talked about earlier in this thread (well, I think this is the same bug - and if it is, the code he provided didn't fix it). Here is exactly when it happens: you're for example the client 1, and you're a spectator following the client 0. The client 0 fires an event (like attacking, or jumping on a vehicle..) while you're watching him. You stop following him. He climbs on a vehicle, and this is when the bug happen: your view begins to shake/lag horribly when this player is in view (or almost) and on the vehicle. As soon as he gets off, your view comes back to normal.

The problem is client-side, and here is the reason: while you are following someone, you receive his events, which calls CG_CheckPlayerstateEvents with the address of cg.snap->ps in the first argument. So the "cent->playerState = ps;" line of this function replaces the cent's (here, client 0) playerState ADDRESS (which should always be in the cgSendPSPool array) with the ADDRESS of the snapshot's playerState. Now you can imagine what happens in CG_Player when the client 0 is attached to the vehicle: his playerState->origin is changed, which also changes the cg.snap->ps.origin, and your view is moved when it shouldn't. It's shaky simply because the cg.snap is alternatively in cg.activeSnapshots[0] and in cg.activeSnapshots[1], so sometimes the snapshot is not modified and the origin is correct.

To make it short: in cg_playerState.c in CG_CheckPlayerstateEvents, comment the line 242:


void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) {

...
//JLF ADDED to hopefully mark events as player event
//cent->playerState = ps;
...
}

I think it's ok to simply comment it, because it seems completely stupid, and the comment above (//JLF ADDED to hopefully mark events as player event) shows it was probably meant to be used on Xbox only (the JLF comments are often related to Xbox stuff). I didn't notice anything weird after commenting it, anyway.

Sorry for the long explanation for a so simple bugfix (1 line to comment, but it took me hours to find it), but I don't think it was obvious it was related to the bug described :)

ensiform
01-07-2007, 12:35 PM
This is an interesting theory, and I really didn't think mine was going to fix it in the first place, though it does force spectators off that vehicle it still isn't right. Good find Soh Raun.

razorace
01-07-2007, 03:02 PM
Nice find Sol Raun. Welcome to the community.

Lathain Valtiel
01-08-2007, 12:48 PM
If that fix works, he just made Kurgan's day.

ensiform
01-20-2007, 09:44 PM
Sometimes vehicle missiles bounce off walls when they should explode right away (IE: atst rockets when ur at a certain angle up against a wall)

Sometimes the muzzle for vehicle weapons is not properly set so like if your in the ATST on siege_hoth and up against some of the relatively small boxes, they still somehow block your shots because the missiles are being created down towards the bottom of the vehicle sometimes.

Shields tend to block projectiles above their visual height and I don't see why they would...

Many times shields are made extremely tiny if placed in some areas like on the bridge on hoth sometimes, or just made with really retarded angles where things don't turn out so nice.

ensiform
01-31-2007, 08:06 PM
*BUMP*

Big bug can be found in lots of places in JKA code especially the anim parsing:

http://www.quakesrc.org/forums/viewtopic.php?p=47222#47222

Read the post below the one in link Timbo has some insight on why its bad ;).

I believe where they have if ( !token || !token[0] ) is fine but having just if ( !token ) is not :S

ensiform
02-04-2007, 07:53 PM
Ambient Sounds Seem To Get Lost After snd_restart and the imfamous Sound bug in siege.

Griswald
02-18-2007, 02:53 PM
slightly cosmetic issue in multiplayer, when a player dies, whatever saber they use for saber1 (which is actually saber[0]) gets reset to default. The reason for this is in CG_BodyQueueCopy, where no eType is being set for the new entity. Although this is a cosmetic issue, further inspection into this seems to show a LOT of issues.

For now, I've fixed the cosmetic issue, and need to work on the remaining issues involved. This is a client side bug, sorry server side mods :(

cg_servercmds.c, function CG_BodyQueueCopy:
Look for:

cent->bodyFadeTime = 0;
cent->bodyHeight = 0;

cent->dustTrailTime = source->dustTrailTime;

trap_G2API_DuplicateGhoul2Instance(source->ghoul2, &cent->ghoul2);

if (source->isRagging)


Change this to:

cent->bodyFadeTime = 0;
cent->bodyHeight = 0;

cent->dustTrailTime = source->dustTrailTime;

//// Griswald -- cent needs an eType to pass on for proper saber model setting.
//// Stock JKA Bug. This is a corpse, so ET_BODY should suffice.
cent->currentState.eType = ET_BODY;

trap_G2API_DuplicateGhoul2Instance(source->ghoul2, &cent->ghoul2);

if (source->isRagging)



in cg_weapons.c, function CG_G2WeaponInstance:
Look for:

if (cent->currentState.eType != ET_PLAYER &&
cent->currentState.eType != ET_NPC)
{
return g2WeaponInstances[weapon];
}

if (cent->currentState.eType == ET_NPC)
{
ci = cent->npcClient;
}


Change this too:

if (cent->currentState.eType != ET_PLAYER &&
cent->currentState.eType != ET_NPC && cent->currentState.eType != ET_BODY)
{
return g2WeaponInstances[weapon];
}

if (cent->currentState.eType == ET_NPC)
{
ci = cent->npcClient;
}


However, the remaining issues still remain, the ONLY saber being checked is saber[0], and this is the only saber being passed into CG_G2WeaponInstance, and is the only saber being checked inside. Possible memory leak anyone?

ensiform
02-19-2007, 03:44 PM
I would suggest using an unused variable in centity_t such as vChatTime for the body and pimp the client number on it, then in CG_G2WeaponInstance you can have the client-info for the proper client.

ensiform
03-07-2007, 12:40 PM
I've found that there is a bug with ClientBegin...

You must catch PERS_SPAWN_COUNT before the ps is cleared, and then copy it back in after being cleared.. Otherwise you will find CG_Respawn on the client is not called like it is supposed to on *every* spawn. This is mostly noticable in siege or if you are trying to call a script on client for every spawn such as I do with Lua.

so do something like

int spawn_count;

copy eflags...
spawn_count = client->ps.persistant[PERS_SPAWN_COUNT];

... clear ps ...

copy eflags back...
client->ps.persistant[PERS_SPAWN_COUNT] = spawn_count;

razorace
03-07-2007, 06:25 PM
it's not copied as part of the normal persistant save?

ensiform
03-07-2007, 06:55 PM
no its in ps.persistant[] not client.pers->

and ClientBegin itself, doesn't actually save anything... I found this while inspecting Enemy Territory as to why CG_Respawn is called in ET but not Q3 or JK at ClientBegin.

razorace
03-08-2007, 02:12 PM
Ah, gotcha. Mind adding that fix to the OJP code?

ensiform
03-08-2007, 02:30 PM
I suppose though my svn build and code isnt exactly capable of being uploaded at the moment. :rolleyes:

Gamall Ida
04-14-2007, 02:43 PM
My 2 cents to this thread: clean ways to counter the q3fill attack (as opposed to counting the number of connections from the same ip during a given period, which works but allows some bots to get in)

The bots' userinfo is not exactly built like that of a regular player ; some info is missing, and other is specific to a fake. So, in ClientConnect(), just check whether Info_ValueForKey(userinfo, "model") for instance, is null or not. If it is, then it's a fake.

Conversely, if Info_ValueForKey (userinfo, "cl_guid"), for instance, is not the empty chain, then it is a fake.

Ways around that fix: for the first one, the attacker can add relevant info directly using q3fill. For the second one, they would need to alter q3fill source code and rebuild it. Either way it should keep the vast majority of attackers away, and I haven't seen any false positive.

dumbledore
04-14-2007, 08:21 PM
yeah as you pointed out the big problem with checking stuff like that is that anyone could rebuild q3fill, and only one guy needs to do it and post it on the internet for it to become a big problem again...

ensiform
04-14-2007, 10:14 PM
Theres also a bug where many of the char arrays are bigger than they need to be in ClientConnect and ClientUserInfoChanged... And some are pointers when they should be sized arrays.

ensiform
04-14-2007, 10:22 PM
My 2 cents to this thread: clean ways to counter the q3fill attack (as opposed to counting the number of connections from the same ip during a given period, which works but allows some bots to get in)

The bots' userinfo is not exactly built like that of a regular player ; some info is missing, and other is specific to a fake. So, in ClientConnect(), just check whether Info_ValueForKey(userinfo, "model") for instance, is null or not. If it is, then it's a fake.

Conversely, if Info_ValueForKey (userinfo, "cl_guid"), for instance, is not the empty chain, then it is a fake.

Ways around that fix: for the first one, the attacker can add relevant info directly using q3fill. For the second one, they would need to alter q3fill source code and rebuild it. Either way it should keep the vast majority of attackers away, and I haven't seen any false positive.

I don't see how cl_guid makes any difference, JA doesn't use it. The latest q3fill lets you append any cvar AFAIK. Plus you can do it without q3fill and just append cvars mostly in commandline if you want to append bogus sh1t.

BTW, IP data cannot be spoofed on ClientConnect() and I have IPSPOOF protection anyway (which is different from # of connections thing).

I took a look at that rtf of ur code pieces and it looks kind of disgusting plus you have some structs or arrays that we don't even see in there... Clean it up if you expect any of the non-hardcore modders here to know what any of it means.

1) You can put the cvars in the cvartable and then you dont need those trap calls or using the ints/floats in the function and just call theVmCvar.integer
2) You're using a lot of strcpy on char XX[size] when that can be rly bad.
3) I think you could probably copy Q_stricmp and make a regular Q_strcmp for strcmp... and stricmp usage should be Q_stricmp because of strcasecmp hack for linux.
4) G_LogPrintf( "Sent command : [%s]\n", text ); should not use this whole thing because G_LogPrintf will take a dump if its big as well. I use Q_vsnprintf now for it though so it doesn't matter.
5) Checking string length could/should be done in G_Say not Cmd_Say_f and Cmd_Tell_f.
6) Your improved log messages don't take account for bots.
I use:
ClientConnect: num [ip] \"name\"\n
BotConnect: num \"name\"\n
ClientRename: num [ip] \"oldname^7\" -> \"newname\"\n
BotRename: num \"oldname^7\" -> \"newname^7\"\n
ClientDisconnect: num [ip] \"name\"\n
BotDisconnect: num \"name\"\n
7) What is the purpose of storing clientNum? You are never a different client number unless you fully disconnect.
8) IP Address in userinfo is lost after ClientConnect usually.

Gamall Ida
04-15-2007, 06:25 AM
I don't see how cl_guid makes any difference, JA doesn't use it. The latest q3fill lets you append any cvar AFAIK.

With model you can append it, with cl_guid it is hardcoded into q3fill, so you can't just append it like that, and the point IS that bots use it while JKA don't. And since you can't append a "null" cl_guid, this will work even if the attacker appends whatever he wants...

As long as nobody modifies q3fill.

I took a look at that rtf of ur code pieces and it looks kind of disgusting plus you have some structs or arrays that we don't even see in there... Clean it up if you expect any of the non-hardcore modders here to know what any of it means.

That's why I didn't post the whole thing here ;) I just posted the idea, which I know, for having tested it thoroughly, works.

The rest is off-topic since it is pertaining to my mod and the way I coded things (which I know is far from perfect ; I'm an amateur, too used to the convenience of C++ classes, and I learned both C and the JKA SDK at the same time I was writing this...).

However, since you are kindly offering your help, I'd be very foolish to ignore it :)

1 Yep, I figured that a little late in the game. But why does Raven use it that way ? In CheckVote, for instance : if (trap_Cvar_VariableIntegerValue("g_gametype") != level.votingGametypeTo)

2 I use it 3 times in ClientConnect, and the buffer is large enough to receive it in all cases. Did you mean I should use Q_strncpyz instead ?

3 "strcasecmp hack for linux" What's that ?

4 Yep. I'll just cut it to a reasonable length. What's Q_vsnprintf ? couldn't find it in the SDK.

5 Why? G_Say and G_SayTo are called only by these two, so cutting it in Cmd_*_f also cuts it for G_Say(To). I've tested q3msgboom with say, say_team and tell ingame, and there is no problem.

6 The whole userinfo is stored in the logs right from the start, so it is stored for bots too. Making the log entry different for bots is a very good idea though :)

7 It is used for autokicking, in the # connect/time case : I need the client nums of the last connected clients, in order to kick them all, and not just the clientNum of the current connecting client.

8 Er. Ok. So ? I never use it anywhere after ClientConnect.

edit : typos

ensiform
04-15-2007, 08:43 PM
1 Yep, I figured that a little late in the game. But why does Raven use it that way ? In CheckVote, for instance :

I'm not really sure, there are some cases that they just didn't want too and this one might even be a Q3 thing not JKA specific... I think it is because there are some cases like after restarts or if its going to restart or if it is a latch cvar, that the value in the vmCvar is not correct however the cvar_t data is always correct so that is probably why.

ensiform
04-22-2007, 01:05 AM
Lightsaber loading code seems wrong on client side...

When a player has a staff saber that you don't have, it should probably load parameters for "dual_1" instead of just the defaults maybe? Because if you've ever played on a JA- server without its client or the extra sabers and somebody is using a staff that you don't have, it looks like people are using staff moves with the default single saber. :<

razorace
04-22-2007, 03:59 AM
It's a good idea, but is there a way to tell if a saber is a staff saber without the .sab file info?

ensiform
04-22-2007, 12:09 PM
Uhhh... I suppose probably not.

Also: I found your bugfix for the Mods Menu:

basejka:
columns 1 2 40 20
ojp:
columns 1 2 40 280

in the LISTBOX for mods part of setup.menu

It seems as though they changed it in the setup.menu for SP in the actual assets but in the SDK, and ui/jamp it seems incorrect.

Edit: again i looked in assets3 and it looks like 1.01 fixed that for basejka too but the menus that come with the SDK are 1.00 or something. :rolleyes:

razorace
04-22-2007, 03:50 PM
Good thing you found it. I had totally forgotten about that. :)

ensiform
04-22-2007, 08:13 PM
Yea that one was stumping me :/

ensiform
08-08-2007, 01:27 AM
CG_INCOMING_CONSOLE_COMMAND

Has issues when you're trying to grab /say cmd...

If you're checking if command contains 'say' and you type message in the chat box or just type in console without /say it will not recognize it as having come from 'say'.

There is nothing that can really be done about this without the engine.

ensiform
08-14-2007, 01:36 PM
For whatever reason I do not know why but they disabled cg_simpleItems for flags...

easy fix:

cg_ents.c, Function: CG_Item, Line: if ( cg_simpleItems.integer && item->giType != IT_TEAM ) {

Comment out the not team type part.

ensiform
09-13-2007, 06:01 PM
Is there a reason why obituary inherits the origin of the player that died? That makes no sense, its just printing a message. It has svf_broadcast anyway... No sense wasting bandwidth. Change self->r.currentOrigin to vec3_origin (0,0,0).

EV_SIEGESPEC can also be changed, as its just setting a a value on the client when sent to the client of te->s.owner. If anyone else thinks of any others, please do let me know. Generally G_TempEntity is what you would look for. Certain things though, do depend on the origin. You wouldn't want to change saber or fire effect stuff for sure. But messages and just simple setting stuff really shouldn't be used. Player Teleport is needed though. I'm also considering whether or not it would be good to change the default behavior of target_screenshake because I just don't see why you would want a screen shake to be a global effect like it is defaulted so. As seen here:

void SP_target_screenshake(gentity_t *ent)
{
G_SpawnFloat( "intensity", "10", &ent->speed );
//intensity of the shake
G_SpawnInt( "duration", "800", &ent->genericValue5 );
//duration of the shake
G_SpawnInt( "globalshake", "1", &ent->genericValue6 );
//non-0 if shake should be global (all clients). Otherwise, only in the PVS.

ent->use = Use_Target_Screenshake;
}

ensiform
09-18-2007, 10:40 PM
Remove the UI_CacheSaberGlowGraphics and static qhandle defines for saber shaders. As the fixme says its busted on vid_restart. Remove instances of this func and move the qhandles into cachedAssets_t and also set them in AssetCache().

ensiform
09-21-2007, 11:32 PM
Why in heck are the item bounding boxes different when dropped from normal?

FinishSpawningItem:

VectorSet (ent->r.mins, -8, -8, -0);
VectorSet (ent->r.maxs, 8, 8, 16);

LaunchItem:

VectorSet (dropped->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS);
VectorSet (dropped->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS);

bg_public.h:

#define ITEM_RADIUS 15 // item sizes are needed for client side pickup detection

:smash:

Not really sure which way people wanna have it.

Tinny
09-22-2007, 05:24 AM
That is weird, do you know how some mods make items physics objects and allows them to be pushed/pulled around?

ensiform
09-22-2007, 03:01 PM
Yes, mine does. Some mods actually create a new launched entity which is really bad, and just hide the original.

I just let them move it around but store the normal position to return to.

Hell, even JA+ puts the stupid hologram on (weapons and powerups) because slider doesn't know how to properly do it, really annoying too cuz you cannot then pick up dropped weapons if you already have it if it was pushed/pulled, which doesn't make any sense at all. Funny may it be, but what if i want to pull ammo from a guy i just killed towards me?

Tinny
09-22-2007, 05:33 PM
What do you do? Have a timer on it that makes it return to a position after it hasn't been affected a while? I tried making items a physics object but for some reason they still don't move around, I tried even editing the force_throw code to allow items to be allowed to move around but no luck.

ensiform
09-22-2007, 05:52 PM
} else if ( em_itemPush.integer && CheckPu****em( push_list[x] ) ) { //rolling and stationary thermal detonators are dealt with below
if ( push_list[x]->item->giType == IT_TEAM ) {
push_list[x]->nextthink = level.time + CTF_FLAG_RETURN_TIME;
push_list[x]->think = ResetItem; // return to original spot after CTF_FLAG_RETURN_TIME ms
} else {
push_list[x]->nextthink = level.time + 30000;
push_list[x]->think = ResetItem; // return to original spot after 30 seconds
}

if ( pull ) {
//pull the item

push_list[x]->s.pos.trType = TR_GRAVITY;
push_list[x]->s.apos.trType = TR_GRAVITY;
VectorScale( forward, -650.0f, push_list[x]->s.pos.trDelta );
VectorScale( forward, -650.0f, push_list[x]->s.apos.trDelta );
push_list[x]->s.pos.trTime = level.time; // move a bit on the very first frame
push_list[x]->s.apos.trTime = level.time; // move a bit on the very first frame
VectorCopy( push_list[x]->r.currentOrigin, push_list[x]->s.pos.trBase );
VectorCopy( push_list[x]->r.currentOrigin, push_list[x]->s.apos.trBase );
push_list[x]->physicsObject = qtrue;
push_list[x]->flags |= FL_BOUNCE_HALF;
} else {
push_list[x]->s.pos.trType = TR_GRAVITY;
push_list[x]->s.apos.trType = TR_GRAVITY;
VectorScale( forward, 650.0f, push_list[x]->s.pos.trDelta );
VectorScale( forward, 650.0f, push_list[x]->s.apos.trDelta );
push_list[x]->s.pos.trTime = level.time; // move a bit on the very first frame
push_list[x]->s.apos.trTime = level.time; // move a bit on the very first frame
VectorCopy( push_list[x]->r.currentOrigin, push_list[x]->s.pos.trBase );
VectorCopy( push_list[x]->r.currentOrigin, push_list[x]->s.apos.trBase );
push_list[x]->physicsObject = qtrue;
push_list[x]->flags |= FL_BOUNCE_HALF;
}
}

I store the original position and such and whether it has spawned originally.
CheckPu****em checks to see if the item is suspended, has a hologram below it, or is flag and check against cvar being 2 to allow dropped flags to move.

Tinny
09-22-2007, 07:33 PM
Oh ok, thanks a ton Ensi.

razorace
09-26-2007, 07:51 PM
Why in heck are the item bounding boxes different when dropped from normal?

Not really sure which way people wanna have it.
I'd use the normal bounding boxes in both cases. The dropped bbox sizes aren't what most people expect.

ensiform
10-09-2007, 07:47 PM
Fixed Bug where maps that have pits but do not have "nodrop" surface down in the pit thus flags that fall/tossed/pushed/pulled down will sit there until they expire.

Above G_RunItem:

// double size of items
static vec3_t itemMins2 = {-16, -16, 0};
static vec3_t itemMaxs2 = {16, 16, 32};

This is what I did in G_RunItem:

Note, the code includes my method of item push/pull checking, and is not necessary for basejka.

Below the following statement:

if ( tr.fraction == 1 ) {
return;
}

I added:

if(ent->item && ((ent->flags & FL_DROPPED_ITEM) || (em_itemPush.integer && ent->think == ResetItem)))
{
int entityList[MAX_GENTITIES];
int i, cnt;
gentity_t *other;
gentity_t *t;
vec3_t mins, maxs;

VectorAdd(ent->r.currentOrigin, itemMins2, mins);
VectorAdd(ent->r.currentOrigin, itemMaxs2, maxs);

cnt = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES);

for( i = 0; i < cnt; i++)
{
other = &g_entities[entityList[i]];

if(other == ent)
{
continue;
}

if(other && other->inuse && !Q_stricmp(other->classname, "trigger_hurt"))
{
if(ent->item->giType == IT_TEAM)
{
Team_FreeEntity(ent);
}
else
{
if(ent->think == ResetItem)
{
ResetItem(ent);
}
else
{
G_FreeEntity(ent);
}
}
return;
}

if(other && other->inuse && (!Q_stricmp(other->classname, "trigger_multiple") || !Q_stricmp(other->classname, "trigger_always") || !Q_stricmp(other->classname, "trigger_once")) && other->target && other->target[0])
{
int hash = BG_StringHashValue(other->target);
t = NULL;
while ((t = G_FindByTargetnameFast(t, other->target, hash)) != NULL)
{
if(t && t->inuse && !Q_stricmp(t->classname, "target_kill"))
{
if(ent->item->giType == IT_TEAM)
{
Team_FreeEntity(ent);
}
else
{
if(ent->think == ResetItem)
{
ResetItem(ent);
}
else
{
G_FreeEntity(ent);
}
}
return;
}
else
{
continue;
}
}
}
}
}

ensiform
10-10-2007, 12:14 AM
The infamous obtained wrong item bug is caused by an issue with the engine because events use eventParm stuff which is sent over the network in 8 bits (0-255 range) where entity numbers can be 0-1024 range (10 bits). This cannot be fixed without modifying the engine sadly.

EV_ITEM_PICKUP sends the item that you picked up's entity number and can without a doubt be higher than 255. So, meh :<

Edit: It may actually be doable without the engine (since this may not be the real problem as previously thought/said).

I'm currently testing it with a modified version of the event with a temp entity instead. This would not work well with server-side only mods of course. I also recommend turning off cg_predictItems with this method, as server is always right.

ensiform
10-25-2007, 10:15 PM
Bug in TIMER_Clear2 that doesn't clear timers on entity 0.

if ( ent && ent->s.number > 0 && ent->s.number < MAX_GENTITIES )

Should be:

if ( ent && ent->s.number >= 0 && ent->s.number < MAX_GENTITIES )

eyen
11-02-2007, 11:31 PM
Don't tell me there's more > 0 bugs ._. I might grep the source later on, unless you've already searched for them.

ensiform
11-03-2007, 09:16 PM
I'm sure there are, and no I really did not. >.<

Mostly in the NPC code you will find a lot of entity 0 stuff.

ensiform
11-03-2007, 10:54 PM
Unique1's Holocron code that is seen in OJP has an out of bounds bug:

if (number_of_holocronpositions > MAX_HOLOCRON_POSITIONS)

Should be >= because array is 0...max-1

ensiform
11-05-2007, 07:50 PM
CG_PointContents should use projected origin and angles in-case water is a moving entity such as draining water from an area.

contents |= trap_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );

should be:

contents |= trap_CM_TransformedPointContents( point, cmodel, cent->lerpOrigin, cent->lerpAngles );

LightNinja
11-06-2007, 07:51 PM
Hey ensiform, could you point me where's the code related to vehicles? I'm gonna have to take a look in order to make my dragon flyable. If you don't know where's it don't worry, its just that I've downloaded recently the SDK and I haven't looked at it yet, I've started programing in c++ in university and now I can understand things on this thread...:)

btw good job.

ensiform
11-08-2007, 05:42 PM
Well you can make vehicles without editing the SDK. I would say start by making it a flying type vehicle for now.

ensiform
11-08-2007, 05:44 PM
CalcEntitySpot has a bug when SPOT_WEAPON is the spot being used...

case SPOT_WEAPON:
//NOTE: automatically takes leaning into account!
if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles ))
{
AngleVectors( ent->NPC->shootAngles, forward, right, up );
}
else
{
AngleVectors( ent->client->ps.viewangles, forward, right, up );
}
CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point );
break;

But what happens when Ent is neither a player nor an NPC?

Answer: AngleVectors is inputted with an invalid first param and thus causes crashing.

Fix:

case SPOT_WEAPON:
if( ent->client )
{
//NOTE: automatically takes leaning into account!
if( ent->NPC && !VectorCompare( ent->NPC->shootAngles, vec3_origin ) && !VectorCompare( ent->NPC->shootAngles, ent->client->ps.viewangles ))
{
AngleVectors( ent->NPC->shootAngles, forward, right, up );
}
else
{
AngleVectors( ent->client->ps.viewangles, forward, right, up );
}
CalcMuzzlePoint( (gentity_t*)ent, forward, right, up, point );
} else
return;
break;

I put the client check there because, NPC's also have a client pointer. And its not like other things are supposed to be using SPOT_WEAPON anyway... But technically you could do else if client do the ps.viewangles and else do ent->r.currentAngles.

DarthDie
11-08-2007, 07:31 PM
That fixes seeker crashing when trying to shoot at shield(or trying todo something with shield) .

ensiform
11-09-2007, 02:30 PM
Wonder why its even targeting the forcefield pickup or the forcefield it self for :S.

ensiform
11-09-2007, 09:36 PM
Download percentage overflow in ui_main.c @ UI_DisplayDownloadInfo.

Bug fixed In Icculus Q3:

http://svn.icculus.org/quake3/trunk/code/ui/ui_main.c?r1=1161&r2=1204&p1=trunk/code/ui/ui_main.c&p2=trunk/code/ui/ui_main.c

ensiform
11-11-2007, 02:01 AM
2 hand lightning effect origin should probably be in middle, not coming from left hand only...

cg_players.c CG_Player:

The following gets changed:

//trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &boltMatrix, tAng, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
if (!gotLHandMatrix)
{
trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &lHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
gotLHandMatrix = qtrue;
}

efOrg[0] = lHandMatrix.matrix[0][3];
efOrg[1] = lHandMatrix.matrix[1][3];
efOrg[2] = lHandMatrix.matrix[2][3];

to:

if ( cent->currentState.torsoAnim == BOTH_FORCE_2HANDEDLIGHTNING_HOLD )
{//find mid point between two bolts
mdxaBone_t rHandMatrix;
trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_rhand, &rHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
if (!gotLHandMatrix)
{
trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &lHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
gotLHandMatrix = qtrue;
}
efOrg[0] = ((lHandMatrix.matrix[0][3] + rHandMatrix.matrix[0][3]) * 0.5f);
efOrg[1] = ((lHandMatrix.matrix[1][3] + rHandMatrix.matrix[1][3]) * 0.5f);
efOrg[2] = ((lHandMatrix.matrix[2][3] + rHandMatrix.matrix[2][3]) * 0.5f);
}
else
{
if (!gotLHandMatrix)
{
trap_G2API_GetBoltMatrix(cent->ghoul2, 0, ci->bolt_lhand, &lHandMatrix, cent->turAngles, cent->lerpOrigin, cg.time, cgs.gameModels, cent->modelScale);
gotLHandMatrix = qtrue;
}
efOrg[0] = lHandMatrix.matrix[0][3];
efOrg[1] = lHandMatrix.matrix[1][3];
efOrg[2] = lHandMatrix.matrix[2][3];
}

Alternatively, you could do it like Single Player and play the effect from both hands. (Though is extra things being drawn). So I compensated for this because, it looked as though and the code said that it was only coming from left, and being in center looks a little bit better.

ensiform
12-21-2007, 11:17 PM
Force Dodge shouldn't work while holding a rocket lock.

Jedi_DodgeEvasion:

if ( self->client->ps.weaponTime > 0 || self->client->ps.forceHandExtend != HANDEXTEND_NONE )
{//in some effect that stops me from moving on my own
return qfalse;
}

=>

if ( self->client->ps.weaponTime > 0 || self->client->ps.forceHandExtend != HANDEXTEND_NONE || self->client->ps.rocketLockTime > 0 )
{//in some effect that stops me from moving on my own
return qfalse;
}

ensiform
01-22-2008, 08:40 PM
cg.snap->ps.fd.forcePowerLevel[] does not contain real data for most powers.

As far as I can tell its only FP_LEVITATION (1) and FP_SEE (14)

razorace
01-23-2008, 12:42 PM
does that cause problems with the client side prediction?

ensiform
01-23-2008, 12:47 PM
Means if you try to check any of the other force power values, you get 0 regardless of what it would be on server.

ensiform
02-12-2008, 06:34 PM
It appears any cvar that is part of userinfo is susceptible to being too large and thus making the userinfo string bigger than 1024 (which would likely cause the IP string and others too may not then be retrieved with trap_GetUserinfo(...) as its not there because it was chopped off).

This can be a hazard because then ban checks cannot be performed. >.<

Fix: Well Luigi has a Windows only patch for it but I haven't heard it tested with q3 and its not supported by Linux.

Other possibilities: Enlarge the buffers in ClientConnect/ClientUserinfoChanged and check that its actual length is not greater than 1024. Check that there is indeed \ip\ in the string (You wouldn't want to also check for the value I guess because the value is lost after first connect.)

ensiform
02-20-2008, 01:25 PM
Info_ValueForKey function calls are quite expensive as they must parse the entire string every time it is called.

Improvement: Use a string hashing method and compare the tokens using Info_NextPair. An example of this can be seen from lucel in the NoQuarter ET mod source.

JediDog
02-24-2008, 07:52 PM
in w_force.c & g_client.c, look for:

Q_strncpyz( forcePowers, Info_ValueForKey (userinfo, "forcepowers"), sizeof( forcePowers ) );

replace with:

if (!(ent->r.svFlags & SVF_BOT))
{
char *s;
s = Info_ValueForKey (userinfo, "forcepowers");
FR_NormalizeForcePowers(s, strlen(s));
strcpy( forcePowers, s );
}
else
{
Q_strncpyz( forcePowers, Info_ValueForKey (userinfo, "forcepowers"), sizeof( forcePowers ) );
}

Why did you add it in both places?
You only need it in one, did it just in case? I believe if you do it in userinfo it prevents it from being changed to it and in w_force.c it changes to it but it doesn't process it. (i think)
I'm only curious.

ensiform
02-24-2008, 07:58 PM
I think you only need to do proper checks in BG_LegalizedForcePowers.

JediDog
02-24-2008, 09:05 PM
bg_misc.c\BG_LegalizedForcePowers is also a good place to put it but the other way is kind of easier in my opinion. :)

Strange... I just tried this force crash 'patch' in w.force.c first and it worked, it didn't crash. I try it in g_client.c\ClientUserInfoChanged and it doesn't prevent the crash. I did everything exactly right but instead of putting it in both places I put it only in userinfo and it isn't working, rawr!!
What would be the cause of this?

ensiform
02-24-2008, 09:28 PM
qboolean BG_LegalizedForcePowers(char *powerOut, int maxRank, qboolean freeSaber, int teamForce, int gametype, int fpDisabled)
{
char powerBuf[128] = {0};
char readBuf[128] = {0};
qboolean maintainsValidity = qtrue;
int powerLen = strlen(powerOut);
int i = 0;
int c = 0;
int allowedPoints = 0;
int usedPoints = 0;
int countDown = 0;

int final_Side;
int final_Powers[NUM_FORCE_POWERS];

//
//blank out the final_Powers array in case we get garbage in powerOut.
memset(final_Powers, 0, sizeof(final_Powers));
//

if (powerLen >= 128)
{ //This should not happen. If it does, this is obviously a bogus string.
//They can have this string. Because I said so.
Q_strncpyz(powerBuf, "7-1-032330000000001333", sizeof(powerBuf));
maintainsValidity = qfalse;
}
else
{
Q_strncpyz(powerBuf, powerOut, sizeof(powerBuf)); //copy it as the original
}

//first of all, print the max rank into the string as the rank
strcpy(powerOut, va("%i-", maxRank));

while (i < 128 && powerBuf[i] && powerBuf[i] != '-')
{
i++;
}
i++;
while (i < 128 && powerBuf[i] && powerBuf[i] != '-')
{
readBuf[c] = powerBuf[i];
c++;
i++;
}
readBuf[c] = 0;
i++;
//at this point, readBuf contains the intended side
final_Side = Q_atoi(readBuf);

if (final_Side != FORCE_LIGHTSIDE &&
final_Side != FORCE_DARKSIDE)
{ //Not a valid side. You will be dark. Because I said so. (this is something that should never actually happen unless you purposely feed in an invalid config)
final_Side = FORCE_DARKSIDE;
maintainsValidity = qfalse;
}

if (teamForce)
{ //If we are under force-aligned teams, make sure we're on the right side.
if (final_Side != teamForce)
{
final_Side = teamForce;
//maintainsValidity = qfalse;
//Not doing this, for now. Let them join the team with their filtered powers.
}
}

//Now we have established a valid rank, and a valid side.
//Read the force powers in, and cut them down based on the various rules supplied.
c = 0;
//
while (i < 128 && powerBuf[i] && powerBuf[i] != '\n' && powerBuf[i] != '\r' //standard sanity checks
&& powerBuf[i] >= '0' && powerBuf[i] <= '3' && c < NUM_FORCE_POWERS)
//while (i < 128 && powerBuf[i] && powerBuf[i] != '\n' && c < NUM_FORCE_POWERS)
//
{
readBuf[0] = powerBuf[i];
readBuf[1] = 0;
final_Powers[c] = Q_atoi(readBuf);
c++;
i++;
}

//final_Powers now contains all the stuff from the string
//Set the maximum allowed points used based on the max rank level, and count the points actually used.
allowedPoints = forceMasteryPoints[maxRank];

i = 0;
while (i < NUM_FORCE_POWERS)
{ //if this power doesn't match the side we're on, then 0 it now.
if (final_Powers[i] &&
forcePowerDarkLight[i] &&
forcePowerDarkLight[i] != final_Side)
{
final_Powers[i] = 0;
//This is only likely to happen with g_forceBasedTeams. Let it slide.
}

if ( final_Powers[i] &&
(fpDisabled & (1 << i)) )
{ //if this power is disabled on the server via said server option, then we don't get it.
final_Powers[i] = 0;
}

i++;
}

if (gametype < GT_TEAM)
{ //don't bother with team powers then
final_Powers[FP_TEAM_HEAL] = 0;
final_Powers[FP_TEAM_FORCE] = 0;
}

usedPoints = 0;
i = 0;
while (i < NUM_FORCE_POWERS) {
countDown = 0;

countDown = final_Powers[i];
//
if(countDown > 3) {
return qfalse; //-1
}
//

while (countDown > 0)
{
usedPoints += bgForcePowerCost[i][countDown]; //[fp index][fp level]
//if this is jump, or we have a free saber and it's offense or defense, take the level back down on level 1
if ( countDown == 1 &&
((i == FP_LEVITATION) ||
(i == FP_SABER_OFFENSE && freeSaber) ||
(i == FP_SABER_DEFENSE && freeSaber)) )
{
usedPoints -= bgForcePowerCost[i][countDown];
}
countDown--;
}

i++;
}

if (usedPoints > allowedPoints)
{ //Time to do the fancy stuff. (meaning, slowly cut parts off while taking a guess at what is most or least important in the config)
int attemptedCycles = 0;
int powerCycle = 2;
int minPow = 0;

if (freeSaber)
{
minPow = 1;
}

maintainsValidity = qfalse;

while (usedPoints > allowedPoints)
{
c = 0;

while (c < NUM_FORCE_POWERS && usedPoints > allowedPoints)
{
if (final_Powers[c] && final_Powers[c] < powerCycle)
{ //kill in order of lowest powers, because the higher powers are probably more important
if (c == FP_SABER_OFFENSE &&
(final_Powers[FP_SABER_DEFENSE] > minPow || final_Powers[FP_SABERTHROW] > 0))
{ //if we're on saber attack, only suck it down if we have no def or throw either
int whichOne = FP_SABERTHROW; //first try throw

if (!final_Powers[whichOne])
{
whichOne = FP_SABER_DEFENSE; //if no throw, drain defense
}

while (final_Powers[whichOne] > 0 && usedPoints > allowedPoints)
{
if ( final_Powers[whichOne] > 1 ||
( (whichOne != FP_SABER_OFFENSE || !freeSaber) &&
(whichOne != FP_SABER_DEFENSE || !freeSaber) ) )
{ //don't take attack or defend down on level 1 still, if it's free
usedPoints -= bgForcePowerCost[whichOne][final_Powers[whichOne]];
final_Powers[whichOne]--;
}
else
{
break;
}
}
}
else
{
while (final_Powers[c] > 0 && usedPoints > allowedPoints)
{
if ( final_Powers[c] > 1 ||
((c != FP_LEVITATION) &&
(c != FP_SABER_OFFENSE || !freeSaber) &&
(c != FP_SABER_DEFENSE || !freeSaber)) )
{
usedPoints -= bgForcePowerCost[c][final_Powers[c]];
final_Powers[c]--;
}
else
{
break;
}
}
}
}

c++;
}

powerCycle++;
attemptedCycles++;

if (attemptedCycles > NUM_FORCE_POWERS)
{ //I think this should be impossible. But just in case.
break;
}
}

if (usedPoints > allowedPoints)
{ //Still? Fine then.. we will kill all of your powers, except the freebies.
i = 0;

while (i < NUM_FORCE_POWERS)
{
final_Powers[i] = 0;
if (i == FP_LEVITATION ||
(i == FP_SABER_OFFENSE && freeSaber) ||
(i == FP_SABER_DEFENSE && freeSaber))
{
final_Powers[i] = 1;
}
i++;
}
usedPoints = 0;
}
}

if (freeSaber)
{
if (final_Powers[FP_SABER_OFFENSE] < 1)
{
final_Powers[FP_SABER_OFFENSE] = 1;
}
if (final_Powers[FP_SABER_DEFENSE] < 1)
{
final_Powers[FP_SABER_DEFENSE] = 1;
}
}
if (final_Powers[FP_LEVITATION] < 1)
{
final_Powers[FP_LEVITATION] = 1;
}

i = 0;
while (i < NUM_FORCE_POWERS)
{
if (final_Powers[i] > FORCE_LEVEL_3)
{
final_Powers[i] = FORCE_LEVEL_3;
}
i++;
}

if (fpDisabled)
{ //If we specifically have attack or def disabled, force them up to level 3. It's the way
//things work for the case of all powers disabled.
//If jump is disabled, down-cap it to level 1. Otherwise don't do a thing.
if (fpDisabled & (1 << FP_LEVITATION))
{
final_Powers[FP_LEVITATION] = 1;
}
if (fpDisabled & (1 << FP_SABER_OFFENSE))
{
final_Powers[FP_SABER_OFFENSE] = 3;
}
if (fpDisabled & (1 << FP_SABER_DEFENSE))
{
final_Powers[FP_SABER_DEFENSE] = 3;
}
}

if (final_Powers[FP_SABER_OFFENSE] < 1)
{
final_Powers[FP_SABER_DEFENSE] = 0;
final_Powers[FP_SABERTHROW] = 0;
}

//We finally have all the force powers legalized and stored locally.
//Put them all into the string and return the result. We already have
//the rank there, so print the side and the powers now.
Q_strcat(powerOut, 128, va("%i-", final_Side));

i = strlen(powerOut);
c = 0;
while (c < NUM_FORCE_POWERS)
{
Q_strncpyz(readBuf, va("%i", final_Powers[c]), sizeof(readBuf));
powerOut[i] = readBuf[0];
c++;
i++;
}
powerOut[i] = 0;

return maintainsValidity;
}

Thats all I use and seems fine.

ensiform
04-08-2008, 01:05 AM
There seems to be an issue with using the cgs.scores1/2 for the team score as it uses data from a ConfigString which appears to be somewhat unreliable during map changes on the client side. For instance: Server running a map that ends and starts changing to new map but you started connecting while old map was still running and then you get to Awaiting Snapshot and start the new map load... You will notice that one or both scores on mini-scoreboard are not quite like they should be (see scoreboard for real score).

-=*Raz0r*=-
09-26-2009, 03:15 PM
Screw letting the dead rest in peace, I want to bring this part of the forum alive again.

Basically, the Q3/JKA memory management is poor - Do not follow their examples!
More at this thread (http://www.japlus.net/phpBB2/viewtopic.php?p=8432#8432) (Old JA+ exploit)

"When you assign dynamic memory as a buffer for anything, be sure to free the damn memory when you are done with it!"
Otherwise, if that function is called enough, the memory pool will overflow and crash the server - Not a good thing at all!
For the most part, it's not a problem..but if us mod authors are allocating memory for whatever reason, free it!

acdcfanbill
09-26-2009, 03:44 PM
Screw letting the dead rest in peace, I want to bring this part of the forum alive again.

Basically, the Q3/JKA memory management is poor - Do not follow their examples!
More at this thread (http://www.japlus.net/phpBB2/viewtopic.php?p=8432#8432) (Old JA+ exploit)

"When you assign dynamic memory as a buffer for anything, be sure to free the damn memory when you are done with it!"
Otherwise, if that function is called enough, the memory pool will overflow and crash the server - Not a good thing at all!
For the most part, it's not a problem..but if us mod authors are allocating memory for whatever reason, free it!

I don't see why it's that poor. It's just lazy modders who are used to languages with memory management systems. Being in C, Q3/JKA figure you are going to clean up your own messes :)

-=*Raz0r*=-
09-26-2009, 04:08 PM
Nothing calling G_Alloc is freeing that memory - that's rather poor to me.
I myself just use malloc and free it - and after all, why shouldn't we =]?

-=*Raz0r*=-
09-27-2009, 10:54 AM
There's a potential infinite loop in G_RadiusDamage

Look for..
if ( dist >= radius ) {
continue;
}

After it, add..
if(ent->health <= 0)
continue;

AFAIK this shouldn't be a problem unless you've added an entity that deals out radius damage and can be destroyed itself.

-=*Raz0r*=-
10-02-2009, 07:07 PM
SKIP TO BOTTOM...
Because of the way chat strings are handled and sent out to each client, players can prepend something to the start of their name to make their chat text appear in the alert area (You know, where it says 'x was y by z' in the top-left)

Easy way to test this is doing '/name .*Blah' and saying anything.

You have a few choices on what you can do..
You can check on every change of their userinfo string, or do something simple and nicer such as this..

In G_SayTo (g_cmds.c) just add in this check before the trap_SendServerCommand call..
for (i=0; i<strlen(name); i++)
{
if (name[i] == '.')
continue;
if (name[i] == '*' && name[i-1] == '.')
return;
break;
}

That will successfully stop them from saying anything if they're trying to use this (minor) exploit.
Other things you can do is alter 'name[i]' and remove that character so they can chat but it won't appear in the alert area.
You could also warn them, change their name, kick them, silence them...whatever you want.
I suppose you can adapt that check to work in ClientUserinfoChanged (g_client.c) if you want.

Happy coding. =]

It appears you can also use '/name **Blah' or whatever, so this 'fix' is useless.
I'll patch this another way some day, unless someone would like to try...

-=*Raz0r*=-
10-02-2009, 08:50 PM
It appears any cvar that is part of userinfo is susceptible to being too large and thus making the userinfo string bigger than 1024 (which would likely cause the IP string and others too may not then be retrieved with trap_GetUserinfo(...) as it's not there because it was chopped off).

This can be a hazard because then ban checks cannot be performed. >.<As mentioned after, we can check that the value does exist, and ban if not.


Fix: Well Luigi has a Windows only patch for it but I haven't heard it tested with q3 and its not supported by Linux.I'm remember hearing there's a side-effect to that 'fix'


Check that there is indeed \ip\ in the string (You wouldn't want to also check for the value I guess because the value is lost after first connect.)For those wondering how to do this, it's rather simple...
Head over to ClientConnect in g_client.c

Declare a variable like so:
char TmpIP[32] = {0};

Adapt some code early on in the function so it looks like this:
// check to see if they are on the banned IP list
value = Info_ValueForKey (userinfo, "ip");
if (!isBot)
Q_strncpyz( TmpIP, value, sizeof(TmpIP) ); // Used later
if ( G_FilterPacket( value ) ) {
return "Banned";
}

Then after the G_ReadSessionData call, chuck in:
if (firstTime && !isBot)
{
if(!TmpIP[0])
{// No IP sent when connecting, probably an unban hack attempt
client->pers.connected = CON_DISCONNECTED;
return "Invalid userinfo detected";
}
Q_strncpyz(client->sess.IP, TmpIP, sizeof(client->sess.IP));
}

You can then use client->sess.IP anywhere in the gameside code for whatever reason.


Another way to prevent q3infoboom would be to patch the engine.
I'm not allowed to 'release' the fix, but it involves hooking SV_ConnectionlessPacket and checking lengths..

-=*Raz0r*=-
10-10-2009, 01:02 PM
5 in a row!

Some of you know of the 'JA Haxxor Toolkit' and its features..
Well, one of these features is a multi-lined name (You can also make it look like someone else said something)

So, an effective way to combat this? Simple.
Adapt your Info_Validate to look like this...
static const char badChars[] = { '\n', '\r', '\"', ';' };
qboolean Info_Validate( const char *s ) {
int i = 0;
for (i=0; i<sizeof(badChars); i++)
if ( strchr( s, badChars[i] ) )
return qfalse;
return qtrue;
}

That should effectively remove carriage returns, line breaks, semicolons and quotation marks from any field in the userinfo string (Client names are kept in their userinfo string)

EDIT: Silly me, you should also perform this check in the say function (G_Say or something in g_cmds.c)

EDIT: I suppose the semi-logical thing would be to remove all instances of those characters in the string, and afterwards check if there are any characters remaining in the string (To prevent a free method of getting a blank name/etc)

-=*Raz0r*=-
09-11-2011, 12:19 AM
There's a missing "firing" animation for the concussion rifle.

In bg_misc.c

int WeaponAttackAnim[WP_NUM_WEAPONS] =
{
BOTH_ATTACK1,//WP_NONE, //(shouldn't happen)

BOTH_ATTACK3,//WP_STUN_BATON,
BOTH_ATTACK3,//WP_MELEE,
BOTH_STAND2,//WP_SABER, //(has its own handling)
BOTH_ATTACK2,//WP_BRYAR_PISTOL,
BOTH_ATTACK3,//WP_BLASTER,
BOTH_ATTACK3,//BOTH_ATTACK4,//WP_DISRUPTOR,
BOTH_ATTACK3,//BOTH_ATTACK5,//WP_BOWCASTER,
BOTH_ATTACK3,//BOTH_ATTACK6,//WP_REPEATER,
BOTH_ATTACK3,//BOTH_ATTACK7,//WP_DEMP2,
BOTH_ATTACK3,//BOTH_ATTACK8,//WP_FLECHETTE,
BOTH_ATTACK3,//BOTH_ATTACK9,//WP_ROCKET_LAUNCHER,
BOTH_THERMAL_THROW,//WP_THERMAL,
BOTH_ATTACK3,//BOTH_ATTACK11,//WP_TRIP_MINE,
BOTH_ATTACK3,//BOTH_ATTACK12,//WP_DET_PACK,
BOTH_ATTACK2,//WP_BRYAR_OLD,

//NOT VALID (e.g. should never really be used):
BOTH_STAND1,//WP_EMPLACED_GUN,


BOTH_ATTACK1//WP_TURRET,
};

Replace with

int WeaponAttackAnim[WP_NUM_WEAPONS] =
{
BOTH_ATTACK1,//WP_NONE, //(shouldn't happen)

BOTH_ATTACK3,//WP_STUN_BATON,
BOTH_ATTACK3,//WP_MELEE,
BOTH_STAND2,//WP_SABER, //(has its own handling)
BOTH_ATTACK2,//WP_BRYAR_PISTOL,
BOTH_ATTACK3,//WP_BLASTER,
BOTH_ATTACK3,//BOTH_ATTACK4,//WP_DISRUPTOR,
BOTH_ATTACK3,//BOTH_ATTACK5,//WP_BOWCASTER,
BOTH_ATTACK3,//BOTH_ATTACK6,//WP_REPEATER,
BOTH_ATTACK3,//BOTH_ATTACK7,//WP_DEMP2,
BOTH_ATTACK3,//BOTH_ATTACK8,//WP_FLECHETTE,
BOTH_ATTACK3,//BOTH_ATTACK9,//WP_ROCKET_LAUNCHER,
BOTH_THERMAL_THROW,//WP_THERMAL,
BOTH_ATTACK3,//BOTH_ATTACK11,//WP_TRIP_MINE,
BOTH_ATTACK3,//BOTH_ATTACK12,//WP_DET_PACK,
BOTH_ATTACK3,//WP_CONCUSSION, //Raz: Fixed bryar pistol animation
BOTH_ATTACK2,//WP_BRYAR_OLD,

//NOT VALID (e.g. should never really be used):
BOTH_STAND1,//WP_EMPLACED_GUN,


BOTH_ATTACK1//WP_TURRET,
};

This array is shared by the server and client, so depending on your use case scenario, you may want to override this "fix" to avoid prediction errors (i.e the server "correcting" your animation halfway through the sequence)

In my case, I am developing a cross-compatible server-side and client-side mod (separately) as an alternative to JA+
I check the serverinfo for "gamename" inside CG_ParseServerinfo, and work out which mod the server is running.

After that, modify PM_Weapon to look like this

#ifndef QAGAME
//Raz: Hacky fix here
int weapon = pm->ps->weapon;
if ( cg.mod != SMOD_JAPP && (pm->ps->weapon == WP_CONCUSSION || pm->ps->weapon == WP_BRYAR_OLD) )
weapon++;
PM_StartTorsoAnim( WeaponAttackAnim[weapon] );
#else
PM_StartTorsoAnim( WeaponAttackAnim[pm->ps->weapon] );
#endif
I have not yet written the code to set the "correct" animation depending on the client's mod. Plugin sniffing is an ugly area.

-=*Raz0r*=-
09-11-2011, 12:38 AM
There is a bug in jamp.exe where connecting to an invalid hostname or IP whilst ingame will shove you out to a black screen, unable to do anything (including open your console). A very ugly situation.

The function in question is CL_Connect_f (0x41D990)
The code in question is:
//Taken from q3
if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) {
Com_Printf ("Bad server address\n");
cls.state = CA_DISCONNECTED;
return;
}
At the very least, it should be setting your connection state to CA_CONNECTING

Ideally, you would rewrite this function to have a different code path if you attempt to connect to a bad hostname or IP whilst ingame.
Another solution, is to patch the opcode setting cls.state to CA_DISCONNECTED

You will have to unlock the code page with VirtualProtect (http://msdn.microsoft.com/en-us/library/aa366898(v=vs.85).aspx) or mprotect (http://linux.die.net/man/2/mprotect)
I suggest writing a wrapper.

UnlockMemory( 0x41DACB, 1 );
*(unsigned char *)0x41DACB = (unsigned char)0x03;
LockMemory( 0x41DACB, 1 );

At the moment this fix is only for Windows, but it is possible to fix on Mac with the right addresses.

With this fix, you will be sent to the "Connecting to someinvalidhostname...1" screen and things will carry on as normal. Not ideal, but it works.

-=*Raz0r*=-
09-11-2011, 12:47 AM
Another bug in jamp.exe where you can't use the Alt + Enter combination to toggle fullscreen. This is intended behaviour, but Raven(?) used the wrong connection state again.

The function in question is MainWndProc (0x454880)
The code in question is:
if ( com_r_fullscreen
&& cl_allowAltEnter
&& (cls_state == CA_DISCONNECTED || cls_state == CA_CONNECTED)
&& cl_allowAltEnter.integer) )
{
Cvar_SetValue( "r_fullscreen", (com_r_fullscreen.integer == 0) );
Cbuf_AddText("vid_restart\n");
}

CA_CONNECTED should actually be CA_ACTIVE ("game views should be displayed")
My fix, however, is slightly hacky but will allow the Alt + Enter combination on all connection states.
It still relies on cl_allowAltEnter being 1

UnlockMemory( 0x454B5A, 2 );
*(unsigned char *)0x454B5A = (unsigned char)0x90; //NOP opcode, skip over the instruction
*(unsigned char *)0x454B5B = (unsigned char)0x90; //NOP opcode, skip over the instruction
LockMemory( 0x454B5A, 2 );

No Mac fix as of yet. I am not sure if this is even applicable for Mac. I don't own one.

-=*Raz0r*=-
09-11-2011, 12:51 AM
A well-known bug, where charged shots cause a dynamic light bug on players.
Easy fix, worth posting.

cg_ents.c, CG_EntityEffects

Adjust the if statement near the end to match the following:
// constant light glow
if ( cent->currentState.constantLight && cent->currentState.eType != ET_PLAYER && cent->currentState.eType != ET_BODY ) {

-=*Raz0r*=-
09-11-2011, 12:59 AM
Credit goes to Didz for finding/fixing this.

In MP, misc_model_static entities' model bounds loading code is incorrect leading to disappearing cliffs and stuff on maps such as t1_surprise and hoth2.

In cg_main.c, search for void CG_CreateModelFromSpawnEnt(cgSpawnEnt_t *ent)

About 44 lines after that inside the function, find:
VectorScaleVector(mins, ent->scale, mins);
VectorScaleVector(maxs, ent->scale, maxs);

Replace these lines with:
//[Invalid Model Bounds Fix]
//VectorScaleVector(mins, ent->scale, mins);
//VectorScaleVector(maxs, ent->scale, maxs);
VectorScaleVector(mins, RefEnt->modelScale, mins);
VectorScaleVector(maxs, RefEnt->modelScale, maxs);
//[/Invalid Model Bounds Fix]

-=*Raz0r*=-
09-11-2011, 01:01 AM
Credit goes to Xycaleth for finding/fixing this.


In JKA, players can jump-crouch through some patches, where it's an angle. This fixes that problem.

In bg_pmove.c, find:
static void PM_CheckDuck (void)
Above that, add:
static qboolean PM_CanStand ( void )
{
qboolean canStand = qtrue;
float x, y;
trace_t trace;

const vec3_t lineMins = { -5.0f, -5.0f, -2.5f };
const vec3_t lineMaxs = { 5.0f, 5.0f, 0.0f };

for ( x = pm->mins[0] + 5.0f; canStand && x <= (pm->maxs[0] - 5.0f); x += 10.0f )
{
for ( y = pm->mins[1] + 5.0f; y <= (pm->maxs[1] - 5.0f); y += 10.0f )
{
vec3_t start = { x, y, pm->maxs[2] };
vec3_t end = { x, y, pm->ps->standheight };

VectorAdd (start, pm->ps->origin, start);
VectorAdd (end, pm->ps->origin, end);

pm->trace (&trace, start, lineMins, lineMaxs, end, pm->ps->clientNum, pm->tracemask);
if ( trace.allsolid || trace.fraction < 1.0f )
{
canStand = qfalse;
break;
}
}
}

return canStand;
}

In the PM_CheckDuck function, find:
else if (pm->ps->pm_flags & PMF_ROLLING)
{
// try to stand up
pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
if (!trace.allsolid)
pm->ps->pm_flags &= ~PMF_ROLLING;
}
Replace with:
else if (pm->ps->pm_flags & PMF_ROLLING)
{
if ( PM_CanStand() )
{
pm->maxs[2] = pm->ps->standheight;
pm->ps->pm_flags &= ~PMF_ROLLING;
}
}
Find:
else
{ // stand up if possible
if (pm->ps->pm_flags & PMF_DUCKED)
{
// try to stand up
pm->maxs[2] = pm->ps->standheight;//DEFAULT_MAXS_2;
pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask );
if (!trace.allsolid)
pm->ps->pm_flags &= ~PMF_DUCKED;
}
}
Replace with:
else
{ // stand up if possible
if (pm->ps->pm_flags & PMF_DUCKED)
{
if ( PM_CanStand() )
{
pm->maxs[2] = pm->ps->standheight;
pm->ps->pm_flags &= ~PMF_DUCKED;
}
}
}

And...that should be it.

Tinny
09-12-2011, 11:00 AM
oh wow, thanks so much!

-=*Raz0r*=-
10-14-2011, 09:46 AM
ITEM_TYPE_EDITFIELD elements will leave insert/overstrike mode on in various occasions.

ui_shared.c -> Item_TextField_HandleKey

Replace
if ( key == A_ENTER || key == A_KP_ENTER || key == A_ESCAPE) {
return qfalse;
}
With
if ( key == A_ENTER || key == A_KP_ENTER || key == A_ESCAPE || (key == A_MOUSE1 && !Rect_ContainsPoint(&item->window.rect, DC->cursorx, DC->cursory) )) {
DC->setOverstrikeMode( qfalse );
return qfalse;
}

-=*Raz0r*=-
10-14-2011, 09:50 AM
ITEM_TYPE_LISTBOX elements (Server browser, hilt selection, etc) can't be scrolled.
Whether you consider this a bug or not is totally your call. I like to scroll through lists :D

ui_shared.c -> Item_ListBox_HandleKey

After
if ( key == A_CURSOR_DOWN || key == A_KP_2 )
{
if (!listPtr->notselectable) {
listPtr->cursorPos++;
if (listPtr->cursorPos < listPtr->startPos) {
listPtr->startPos = listPtr->cursorPos;
//JLF
#ifndef _XBOX
return qfalse;
#endif
}
if (listPtr->cursorPos >= count) {
listPtr->cursorPos = count-1;
return qfalse;
}
if (listPtr->cursorPos >= listPtr->startPos + viewmax) {
listPtr->startPos = listPtr->cursorPos - viewmax + 1;
}
item->cursorPos = listPtr->cursorPos;
DC->feederSelection(item->special, item->cursorPos, NULL);
}
else {
listPtr->startPos++;
if (listPtr->startPos > max)
listPtr->startPos = max;
}
return qtrue;
}
Add
if ( key == A_MWHEELUP )
{
listPtr->startPos -= ((int)item->special == FEEDER_Q3HEADS) ? viewmax : 1;
if (listPtr->startPos < 0)
{
listPtr->startPos = 0;
Display_MouseMove(NULL, DC->cursorx, DC->cursory);
return qfalse;
}
Display_MouseMove(NULL, DC->cursorx, DC->cursory);
return qtrue;
}
if ( key == A_MWHEELDOWN )
{
listPtr->startPos += ((int)item->special == FEEDER_Q3HEADS) ? viewmax : 1;
if (listPtr->startPos > max)
{
listPtr->startPos = max;
Display_MouseMove(NULL, DC->cursorx, DC->cursory);
return qfalse;
}
Display_MouseMove(NULL, DC->cursorx, DC->cursory);
return qtrue;
}


Updated: Fixed for FEEDER_Q3HEADS to skip an entire row (16th October 2011)
Updated: return qfalse if there's no more to scroll, to prevent the sound from playing (10th November 2011)
Updated: Forcefully update the mouse position when scrolling so the proper listbox entry has focus (12th November 2011)

-=*Raz0r*=-
10-14-2011, 10:17 AM
The name field in the profile customisation screen (http://i499.photobucket.com/albums/rr357/amraz0r/c1062ec0.jpg) will not allow more than 26 characters, despite the actual limit being 36 characters.
Furthermore, overflowing this then changing your name results in some...odd behaviour.

The first part of this fix is in the ui/jamp/ingame_player.menu
Adjust this part
itemDef
{
name namefield
type ITEM_TYPE_EDITFIELD
style 0
text @MENUS_NAME1
cvar "ui_Name"
maxchars 26
To match
itemDef
{
name namefield
type ITEM_TYPE_EDITFIELD
style 0
text @MENUS_NAME1
cvar "ui_Name"
maxchars 35
36-1 characters to account for the null-terminator, if I am correct.


ui_main.c -> UI_Update

Replace
if (Q_stricmp(name, "ui_SetName") == 0) {
trap_Cvar_Set( "name", UI_Cvar_VariableString("ui_Name"));
} else if (Q_stricmp(name, "ui_setRate") == 0) {
With
if ( !Q_stricmp( name, "ui_SetName" ) )
{
char buf[36] = { 0 };
Q_strncpyz( buf, UI_Cvar_VariableString( "ui_Name" ), sizeof( buf ) );
trap_Cvar_Set( "name", buf );
}
else if (Q_stricmp(name, "ui_setRate") == 0) {

Replace
else if (Q_stricmp(name, "ui_GetName") == 0)
{
trap_Cvar_Set( "ui_Name", UI_Cvar_VariableString("name"));
}
With
else if ( !Q_stricmp( name, "ui_GetName" ) )
{
char buf[36] = { 0 };
Q_strncpyz( buf, UI_Cvar_VariableString( "name" ), sizeof( buf ) );
trap_Cvar_Set( "ui_Name", buf );
}


ui_main.c -> _UI_Init

Replace
trap_Cvar_Register(NULL, "ui_name", UI_Cvar_VariableString("name"), CVAR_INTERNAL ); //get this now, jic the menus change again trying to setName before getName
With
{
char buf[36] = { 0 };
Q_strncpyz( buf, UI_Cvar_VariableString( "name" ), sizeof( buf ) );
trap_Cvar_Register( NULL, "ui_Name", buf, CVAR_INTERNAL );
}

-=*Raz0r*=-
11-12-2011, 06:16 AM
There's an infinite loop in Cmd_FollowCycle_f (g_cmds.c) that can easily be used to attack an unpatched server.

To reproduce the infinite loop:
Join a server (Preferrably FFA?) where there are no in-game players (Spectators are fine)
/team follow1
Click to cycle through clients

Replace it with this version, also containing this (http://www.lucasforums.com/showthread.php?p=2142829#post2142829) fix

void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
int clientnum;
int original;
qboolean looped = qfalse;

// if they are playing a tournement game, count as a loss
if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
&& ent->client->sess.sessionTeam == TEAM_FREE ) {\
//WTF???
ent->client->sess.losses++;
}
// first set them to spectator
if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) {
SetTeam( ent, "spectator" );
}

if ( dir != 1 && dir != -1 ) {
G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
}

clientnum = ent->client->sess.spectatorClient;
original = clientnum;

do {
clientnum += dir;
if ( clientnum >= level.maxclients )
{
//Raz: Avoid /team follow1 crash
if ( looped )
{
clientnum = original;
break;
}
else
{
clientnum = 0;
looped = qtrue;
}
}
if ( clientnum < 0 ) {
if ( looped )
{
clientnum = original;
break;
}
else
{
clientnum = level.maxclients - 1;
looped = qtrue;
}
}

// can only follow connected clients
if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) {
continue;
}

// can't follow another spectator
if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) {
continue;
}

//ensiform's fix
// can't follow another spectator
if ( level.clients[ clientnum ].tempSpectate >= level.time ) {
return;
}

// this is good, we can use it
ent->client->sess.spectatorClient = clientnum;
ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
return;
} while ( clientnum != original );

// leave it where it was
}

I'm sure the loop could be written more elegantly, but this will suffice.

-=*Raz0r*=-
11-15-2011, 02:03 AM
Prediction error after spectating somebody who is on an ET_MOVER then switching to roaming mode.

Modify the start of CG_AdjustPositionForMover (cg_ents.c) to match:

void CG_AdjustPositionForMover( const vec3_t in, int moverNum, int fromTime, int toTime, vec3_t out ) {
centity_t *cent;
vec3_t oldOrigin, origin, deltaOrigin;
vec3_t oldAngles, angles, deltaAngles;

if ( cg.predictedPlayerState.persistant[PERS_TEAM] == TEAM_SPECTATOR )
{//Don't bother if we're a spectator
VectorCopy( in, out );
return;
}

-=*Raz0r*=-
07-02-2012, 12:23 PM
Weird behaviour in UI code causes onOpen {} events to be triggered on the parent item of the item you're closing (via out-of-bounds click)
Example: Ingame menu -> click "About" -> Click "Setup" -> the onOpen event of the "ingame" menu would be fired.
This is only an issue if you have your own menus that use these events. Personally I'm using transitions in all my menus, which were being triggered at the wrong time.

In ui_shared.c -> Menus_HandleOOBClick

Comment out "Menus_Activate(&Menus[i]);" in the for loop.

for (i = 0; i < menuCount; i++) {
if (Menu_OverActiveItem(&Menus[i], DC->cursorx, DC->cursory)) {
Menu_RunCloseScript(menu);
menu->window.flags &= ~(WINDOW_HASFOCUS | WINDOW_VISIBLE);
// Menus_Activate(&Menus[i]);
Menu_HandleMouseMove(&Menus[i], DC->cursorx, DC->cursory);
Menu_HandleKey(&Menus[i], key, down);
}
}

-=*Raz0r*=-
07-02-2012, 12:27 PM
JA's font width calculation is horrible broken, especially when using abnormal (anything but 1.0) scales.
The issue is in the engine, RE_Font_StrLenPixels uses integer precision for the return value, causing visible data loss.

The way I'm currently working around this is by replacing trap_R_Font_StrLenPixels with:
float trap_R_Font_StrLenPixels(const char *text, const int iFontIndex, const float scale)
{
//Raz: HACK! RE_Font_StrLenPixels only works semi-correctly with 1.0f scale
float width = (float)syscall( CG_R_FONT_STRLENPIXELS, text, iFontIndex, PASSFLOAT(1.0f));
return width * scale;
}

Note I'm using a float return value - you'll have to adjust function prototypes and usage all around the code to use floats for best results.
I also use a large custom font to help with scaling.

This is not a fix, but it does help tremendously when trying to center-align text in both cgame and ui

Theoretically you could replace all text rendering with your own OpenGL code and use the bmfont library. If you wanted to D:

ensiform
10-31-2012, 11:30 PM
There is a bug in jamp.exe where connecting to an invalid hostname or IP whilst ingame will shove you out to a black screen, unable to do anything (including open your console). A very ugly situation.

The function in question is CL_Connect_f (0x41D990)
The code in question is:
//Taken from q3
if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) {
Com_Printf ("Bad server address\n");
cls.state = CA_DISCONNECTED;
return;
}
At the very least, it should be setting your connection state to CA_CONNECTING

Ideally, you would rewrite this function to have a different code path if you attempt to connect to a bad hostname or IP whilst ingame.
Another solution, is to patch the opcode setting cls.state to CA_DISCONNECTED

You will have to unlock the code page with VirtualProtect (http://msdn.microsoft.com/en-us/library/aa366898(v=vs.85).aspx) or mprotect (http://linux.die.net/man/2/mprotect)
I suggest writing a wrapper.

UnlockMemory( 0x41DACB, 1 );
*(unsigned char *)0x41DACB = (unsigned char)0x03;
LockMemory( 0x41DACB, 1 );

At the moment this fix is only for Windows, but it is possible to fix on Mac with the right addresses.

With this fix, you will be sent to the "Connecting to someinvalidhostname...1" screen and things will carry on as normal. Not ideal, but it works.

This fix is actually wrong.

The problem is raven fail coding in the UI:

(Main menu is never actually loaded because you were in the server last which will tell UI_Init to be (qtrue) on ingameload so it will only load ingame related menus. (This is fine however)

When it goes to try to revert back to main menu with setactivemenu UIMENU_MAIN:


if (uiInfo.inGameLoad)
{
// UI_LoadNonIngame();
}

because of this, it fails to actually "activate" main.