need help with ETPro/RTCW style target_locations


(jet Pilot) #1

I’ve implemented target_location functionality in my mod, and it seems to work great as long as there is only one person on each team.
Whenever another player joins the team, the previous player’s location all list as unknown, or fall back to the grid style, eg (D,1).

Here are some of my changes, as you can tell I’m sticking closely to the RTCW method.
I’ve left out all the clientside code, since the error seems to be coming from the server.

in bg_public.h, modified/added the following


#define CS_LOCATIONS	( CS_STRINGS + MAX_CSSTRINGS )
#define CS_MAX		   ( CS_LOCATIONS + MAX_LOCATIONS )

in g_target.c


void SP_target_location( gentity_t *self )
{
//	G_Printf( S_COLOR_YELLOW "WARNING: target_location entities are now obsolete. Please remove ASAP
" );
//	G_FreeEntity( self );
// jet Pilot - un-obsolete-ize this
	self->think = target_location_linkup;
	self->nextthink = level.time + 1000;

	G_SetOrigin( self, self->s.origin );
}

also in g_target.c


// jet Pilot - derived from RTCW location code
void target_location_linkup(gentity_t *ent)
{
	int i, n;	
	static int total = 0;

	total++;
	if (level.locationLinked) 
	{
		Com_Printf("^9target_location_linkup ^3WARNING: linkup already completed. ^9(%d total locations)
", total);
		return;
	}

	level.locationLinked	= qtrue;
	level.locationHead		= NULL;

	trap_SetConfigstring( CS_LOCATIONS, "unknown" );

	for (ent = g_entities, i = 0, n = 1; i < level.num_entities; ent++, i++) 
	{
		if (ent->classname && !Q_stricmp(ent->classname, "target_location")) 
		{			
			trap_SetConfigstring( CS_LOCATIONS + n, ent->message );
			ent->health			= n++; 
			ent->nextTrain		= level.locationHead;
			level.locationHead	= ent;
		}
	}
}

in g_team.c, in the function void TeamplayInfoMessage I’ve changed the following code


Com_sprintf( entry, sizeof(entry), " %i %i %i %i %i", level.sortedClients[i], player->client->pers.teamState.location[0], player->client->pers.teamState.location[1], h, player->s.powerups);

to


// jet Pilot - send locationIndex to clients, (last on list)
Com_sprintf( entry, sizeof(entry), " %i %i %i %i %i %i", level.sortedClients[i], player->client->pers.teamState.location[0], player->client->pers.teamState.location[1], h, player->s.powerups, player->client->pers.teamState.locationIndex );

also in g_team.c,in the function CheckTeamStatus
after


ent->client->pers.teamState.location[0] = (int)ent->r.currentOrigin[0];
ent->client->pers.teamState.location[1] = (int)ent->r.currentOrigin[1];
//added
loc = Team_GetLocation( ent );
if (loc) ent->client->pers.teamState.locationIndex = loc->health;
else	ent->client->pers.teamState.locationIndex = 0;

added these functions


gentity_t *Team_GetLocation(gentity_t *ent)
{
	gentity_t	*curLoc;
	gentity_t	*bestLoc	= NULL;
	float		bestdist	= 200000000;
	float		len;
	vec3_t		origin;	

	VectorCopy( ent->r.currentOrigin, origin );

	for (curLoc = level.locationHead; curLoc; curLoc = curLoc->nextTrain)
	{
		VectorSubtract( ent->r.currentOrigin, curLoc->r.currentOrigin, lenVec );
		len = VectorLength( lenVec );

		if ( len > bestdist || !trap_InPVS( origin, curLoc->r.currentOrigin )) 
		{
			continue;
		}

		bestdist = len;
		bestLoc	 = curLoc;
	}

	return bestLoc;
}


qboolean Team_GetLocationMsg(gentity_t *ent, char *loc, int loclen)
{
	gentity_t	*curLoc;
	gentity_t	*bestLocLoc	= NULL;
	float		bestdist	= 200000000;
	float		len;
	vec3_t		origin;	

	VectorCopy( ent->r.currentOrigin, origin );

	for (curLoc = level.locationHead; curLoc; curLoc = curLoc->nextTrain)
	{
		VectorSubtract( ent->r.currentOrigin, curLoc->r.currentOrigin, lenVec );
		len = VectorLength( lenVec );

		if ( len > bestdist || !trap_InPVS( origin, curLoc->r.currentOrigin )) 
		{
			continue;
		}

		bestdist = len;
		bestLoc	 = curLoc;
	}
	
	if (!bestLoc)
	{
		return qfalse;
	}
	
	Com_sprintf(loc, loclen, "[lon]%s[lof]", bestLoc->message);

	return qtrue;
}

in g_cmds.c, in the function G_Say, I’ve modified SAY_TEAM like this
(note the fallback to the grid coordinates if target_location code fails)


case SAY_TEAM:
			localize = qtrue;			
			G_LogPrintf( "sayteam: %s: %s
", ent->client->pers.netname, text );
			if (Team_GetLocationMsg(ent, location, sizeof(location)))
			{
				Com_sprintf (name, sizeof(name), "[lof](%s%c%c) (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location);
			}
			else 
			{
				loc = BG_GetLocationString( ent->r.currentOrigin );
				Com_sprintf (name, sizeof(name), "[lof](%s%c%c) (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, loc);
			}
			color = COLOR_CYAN;
			break;

I’ve got no clue why the check works fine until another player joins the team, and it seems to work fine for that player, just not the first player. I’ve only tested it with two players so far, so I don’t know if the third player will break the 2nd as well (and so on)

Any help for anyone would be great, and if anyone who has it actually working could give me some pointers… that would be awesome.


(SCDS_reyalP) #2

You might wan’t to ask one of the etpro coders in #etpro on freenode, or on their forums.


(Demolama) #3

I did mine a different way… yea probably better to ask the etpro team… Im sure they did theirs the way you did


(Lanz) #4

I just implemented it using this code, I was about to do it anyway and figured I could just rollback if I couldn’t solve any particular bugs it would produce. Anyway, I had to add the obvious stuff missing from the code like a couple of variables to some of the structs etc and some simple code client side, I also created a very simple map with only one target_location and tested it with three clients locally on my pc and everything worked as it should!

I’m going to make a map with a couple of more target_locations and see if I can reproduce the problem you where having but it seems like you must have added something else somewhere that fucks it up somehow.


(kamikazee) #5

Lesson: tag the spots you changed so you can find those again without diffing against the plain sources.


(Lanz) #6

Ok, changed the test map so it had a bunch of target_locations and it still worked like it should.


(jet Pilot) #7

Odd…
Kamikazee: I always comment my code, kamikazee, undoing my changes isn’t a problem, I just wanted to get it working properly.
Lanz: you say you implemented it using the code I posted? If so, that’s good news, and I have been know to screw up my code before.
Demoman: How differently did you do yours? Just curious, I’m looking for the most efficient method.


(Lanz) #8

Yeah I used this code, no problems with it at all. I’m in the process of altering it to suit my mod and changing a couple of nitpicking things, but the first version of it was a straight implementation from it.


(ensiform) #9

G_SayTo should have some stuff with locMsg in it should it not?


(jet Pilot) #10

ensiform: it does. If the check for target_locations is successful, it appends the location name to the players name; if not, it appends the player’s coordinates to his name. the resulting string is then used in place of the regular netname.

Lanz: Glad to hear it, makes me less worried about some nonsense. Did you leave MAX_LOCATIONS set to 256? I had changed it to 64, afraid to exceed MAX_CONFIGSTRINGS, I did get an out of range error when another player joined, though, I’ll set it back to 256 - see if that helps. I’d love to see your location code when you finish it, if that’s not a problem.


(Lanz) #11

Yeah exceeding MAX_CONFIGSTRING is not a good idea and changing MAX_CONFIGSTRING gives me the chill, as q_shared is not the place to change things in. I left it at 93 for now since that was what was left. If I find that to not be enough I guess I have to add more locations to each config string instead and build up a list for faster access on the client. Would save some config strings for other things for later too even.

I can post the location code later when it’s done, it’s not gonna be that big of a differance though. It’s maninly two things I’m gonna add, custom colors (dunno if I’ll just add the color codes direct or as in q3/rtcw via the count value) and ability to load in locations from a seperate file. I don’t wanna bulk my mod and release all the maps again just for this.


(jet Pilot) #12

I took out the color code in count, cause I want to set it up so the mapper/whoever can just use ET color codes in message (ie ^4Allied ^1Spawn)
I’d love to see the external file implementation, with the option of allowing a client to have his own custom locations even if the server doesn’t have any.


(Lanz) #13

I don’t think that would be possible on a pure server. Since the only files read by the client are the pk3’s reported availabe by the server. Not other external files (besides the dll’s) are read AFAIK.


(kamikazee) #14

I believe the location system sits on the server side so no need to get the files to the client.
It is possible to read external files though, I’ve seen bot-mods which read .nod files for bot path-finding.


(SCDS_reyalP) #15

Not true. Certain files are read outside .pk3 files first (.dat is one). I’m 99% sure etpro allows clients to define their own location.dat files

See http://bani.anime.net/banimod/forums/viewtopic.php?t=5089


(jet Pilot) #16

if nothing else, it could be stored in a cfg-type file, with coords and a message

on a side note, it seems my bug was caused client-side :banghead:
I forgot to change the definition of NUMARGS from 5 to 6, right before CG_ParseTeamInfo (in cg_servercmds.c as I was sending an extra argument in tinfo. It worked fine with one client, but when more joined up, it mucked up the order, occasionally giving me a bad index error (using the player’s health as the index, for example)

the updated code here is


#define NUMARGS 6
static void CG_ParseTeamInfo( void ) {
	int		i;
	int		client;
	int		numSortedTeamPlayers;

	numSortedTeamPlayers = atoi( CG_Argv( 1 ) );

	for ( i = 0 ; i < numSortedTeamPlayers ; i++ )
	{
		client = atoi( CG_Argv( i * NUMARGS + 2 ) );

		cgs.clientinfo[	client ].location[0] =			atoi( CG_Argv( i * NUMARGS + 3 ) );
		cgs.clientinfo[	client ].location[1] =			atoi( CG_Argv( i * NUMARGS + 4 ) );
		cgs.clientinfo[	client ].health =				atoi( CG_Argv( i * NUMARGS + 5 ) );
		cgs.clientinfo[	client ].powerups =				atoi( CG_Argv( i * NUMARGS + 6 ) );
		// jet Pilot - grab locationIndex from server
		cgs.clientinfo[ client ].locationIndex =		atoi( CG_Argv( i * NUMARGS + 7 ) );
	}
}

(Lanz) #17

Not true. Certain files are read outside .pk3 files first (.dat is one). I’m 99% sure etpro allows clients to define their own location.dat files

See http://bani.anime.net/banimod/forums/viewtopic.php?t=5089[/quote]

Ah ok, you learn something new every day, I didn’t think they opened up all files with the .dat extension but even better for things like this.

jet Pilot: nice to see that you solved it.


(Demolama) #18

very different… didnt bother using any rtcw code

I made an array of a struct holding location names and the x y z origins and a bool to see if it was already linked… this was added so I didnt try to read a .dat file if the map already used target_locations

read in target_locations into the struct if present… if not read from the .dat file

to find the correct location name for your current location… just used a trig formula for finding locations in a 3d enviroment… I just compared the xyz in the struct to your current xyz

once found I just passed the location name string to the client being sure to add a character for spaces and then just take them out after it was passed over to the client

Im sure rtcw’s method is more efficent… since its not passing a string from the server to the client… too much buffer needed in my code.

this of course leaves all the location names up to the server side…unlike etpro where you can rename you .dat and add your own location names on the client side


(KillerWhale) #19

Wouldn’t it be faster to just use the etpro .dat location files on the clientside. Sending locations strings over the net using configstrings or directly seems a bit slow.
What is the advantage of using the target_location entities system instead of a .dat location file system?
How many maps have target_location entities in them anyway?

You can implement the .dat file system pretty much completely clientsided, you only have to make 2 changes serversided:

  1. Send over the third (Z) player location coordinate to the client in “TeamplayInfoMessage” (g_team.c).
  2. The say_team/say_buddy messages have coordinates added to them by the server, you’ll have to make them clientside like vsay messages are done.