summaryrefslogtreecommitdiff
path: root/src/game/g_client.c
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /src/game/g_client.c
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'src/game/g_client.c')
-rw-r--r--src/game/g_client.c1428
1 files changed, 467 insertions, 961 deletions
diff --git a/src/game/g_client.c b/src/game/g_client.c
index c77d4f7..3481af9 100644
--- a/src/game/g_client.c
+++ b/src/game/g_client.c
@@ -1,13 +1,14 @@
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2006 Tim Angus
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
This file is part of Tremulous.
Tremulous is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation; either version 2 of the License,
+published by the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
Tremulous is distributed in the hope that it will be
@@ -16,8 +17,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with Tremulous; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+along with Tremulous; if not, see <https://www.gnu.org/licenses/>
+
===========================================================================
*/
@@ -81,156 +82,35 @@ void SP_info_human_intermission( gentity_t *ent )
/*
===============
-G_OverflowCredits
-===============
-*/
-void G_OverflowCredits( gclient_t *doner, int credits )
-{
- int i;
- int maxCredits;
- int clientNum;
-
- if( !g_creditOverflow.integer )
- return;
-
- if( doner->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- {
- maxCredits = ALIEN_MAX_KILLS;
- clientNum = level.lastCreditedAlien;
- }
- else if( doner->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
- {
- maxCredits = HUMAN_MAX_CREDITS;
- clientNum = level.lastCreditedHuman;
- }
- else
- {
- return;
- }
-
- if( g_creditOverflow.integer == 1 )
- {
- // distribute to everyone on team
- gentity_t *vic;
-
- i = 0;
- while( credits > 0 && i < level.maxclients )
- {
- i++;
- clientNum++;
- if( clientNum >= level.maxclients )
- clientNum = 0;
-
- vic = &g_entities[ clientNum ];
- if( vic->client->ps.stats[ STAT_PTEAM ] != doner->ps.stats[ STAT_PTEAM ] ||
- vic->client->ps.persistant[ PERS_CREDIT ] >= maxCredits )
- continue;
-
- if( vic->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
- level.lastCreditedAlien = clientNum;
- else
- level.lastCreditedHuman = clientNum;
-
- if( vic->client->ps.persistant[ PERS_CREDIT ] + credits > maxCredits )
- {
- credits -= maxCredits - vic->client->ps.persistant[ PERS_CREDIT ];
- vic->client->ps.persistant[ PERS_CREDIT ] = maxCredits;
- }
- else
- {
- vic->client->ps.persistant[ PERS_CREDIT ] += credits;
- return;
- }
- }
- }
- else if( g_creditOverflow.integer == 2 )
- {
- // distribute by team rank
- gclient_t *cl;
-
- for( i = 0; i < level.numPlayingClients && credits > 0; i++ )
- {
- // get the client list sorted by rank
- cl = &level.clients[ level.sortedClients[ i ] ];
- if( cl->ps.stats[ STAT_PTEAM ] != doner->ps.stats[ STAT_PTEAM ] ||
- cl->ps.persistant[ PERS_CREDIT ] >= maxCredits )
- continue;
-
- if( cl->ps.persistant[ PERS_CREDIT ] + credits > maxCredits )
- {
- credits -= maxCredits - cl->ps.persistant[ PERS_CREDIT ];
- cl->ps.persistant[ PERS_CREDIT ] = maxCredits;
- }
- else
- {
- cl->ps.persistant[ PERS_CREDIT ] += credits;
- return;
- }
- }
- }
-}
-
-/*
-===============
G_AddCreditToClient
===============
*/
void G_AddCreditToClient( gclient_t *client, short credit, qboolean cap )
{
+ int capAmount;
+
if( !client )
return;
- //if we're already at the max and trying to add credit then stop
- if( cap )
- {
- if( client->pers.teamSelection == PTE_ALIENS )
- {
- if( client->pers.credit >= ALIEN_MAX_KILLS &&
- credit > 0 )
- {
- G_OverflowCredits( client, credit );
- return;
- }
- }
- else if( client->pers.teamSelection == PTE_HUMANS )
- {
- if( client->pers.credit >= HUMAN_MAX_CREDITS &&
- credit > 0 )
- {
- G_OverflowCredits( client, credit );
- return;
- }
- }
- }
-
- client->pers.credit += credit;
-
- if( cap )
+ if( cap && credit > 0 )
{
- if( client->pers.teamSelection == PTE_ALIENS )
+ capAmount = client->pers.teamSelection == TEAM_ALIENS ?
+ ALIEN_MAX_CREDITS : HUMAN_MAX_CREDITS;
+ if( client->pers.credit < capAmount )
{
- if( client->pers.credit > ALIEN_MAX_KILLS )
- {
- G_OverflowCredits( client, client->ps.persistant[ PERS_CREDIT ] - ALIEN_MAX_KILLS );
- client->pers.credit = ALIEN_MAX_KILLS;
- }
- }
- else if( client->pers.teamSelection == PTE_HUMANS )
- {
- if( client->pers.credit > HUMAN_MAX_CREDITS )
- {
- G_OverflowCredits( client, client->ps.persistant[ PERS_CREDIT ] - HUMAN_MAX_CREDITS );
- client->pers.credit = HUMAN_MAX_CREDITS;
- }
+ client->pers.credit += credit;
+ if( client->pers.credit > capAmount )
+ client->pers.credit = capAmount;
}
}
+ else
+ client->pers.credit += credit;
if( client->pers.credit < 0 )
client->pers.credit = 0;
- // keep PERS_CREDIT in sync if not following
- if( client->sess.spectatorState != SPECTATOR_FOLLOW )
- client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
+ // Copy to ps so the client can access it
+ client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
}
@@ -255,8 +135,8 @@ qboolean SpotWouldTelefrag( gentity_t *spot )
gentity_t *hit;
vec3_t mins, maxs;
- VectorAdd( spot->s.origin, playerMins, mins );
- VectorAdd( spot->s.origin, playerMaxs, maxs );
+ VectorAdd( spot->r.currentOrigin, playerMins, mins );
+ VectorAdd( spot->r.currentOrigin, playerMaxs, maxs );
num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
for( i = 0; i < num; i++ )
@@ -270,75 +150,6 @@ qboolean SpotWouldTelefrag( gentity_t *spot )
return qfalse;
}
-/*
-================
-G_SelectNearestDeathmatchSpawnPoint
-
-Find the spot that we DON'T want to use
-================
-*/
-#define MAX_SPAWN_POINTS 128
-gentity_t *G_SelectNearestDeathmatchSpawnPoint( vec3_t from )
-{
- gentity_t *spot;
- vec3_t delta;
- float dist, nearestDist;
- gentity_t *nearestSpot;
-
- nearestDist = 999999;
- nearestSpot = NULL;
- spot = NULL;
-
- while( (spot = G_Find( spot, FOFS( classname ), "info_player_deathmatch" ) ) != NULL )
- {
- VectorSubtract( spot->s.origin, from, delta );
- dist = VectorLength( delta );
-
- if( dist < nearestDist )
- {
- nearestDist = dist;
- nearestSpot = spot;
- }
- }
-
- return nearestSpot;
-}
-
-
-/*
-================
-G_SelectRandomDeathmatchSpawnPoint
-
-go to a random point that doesn't telefrag
-================
-*/
-#define MAX_SPAWN_POINTS 128
-gentity_t *G_SelectRandomDeathmatchSpawnPoint( void )
-{
- gentity_t *spot;
- int count;
- int selection;
- gentity_t *spots[ MAX_SPAWN_POINTS ];
-
- count = 0;
- spot = NULL;
-
- while( ( spot = G_Find( spot, FOFS( classname ), "info_player_deathmatch" ) ) != NULL )
- {
- if( SpotWouldTelefrag( spot ) )
- continue;
-
- spots[ count ] = spot;
- count++;
- }
-
- if( !count ) // no spots that won't telefrag
- return G_Find( NULL, FOFS( classname ), "info_player_deathmatch" );
-
- selection = rand( ) % count;
- return spots[ selection ];
-}
-
/*
===========
@@ -347,7 +158,7 @@ G_SelectRandomFurthestSpawnPoint
Chooses a player start, deathmatch start, etc
============
*/
-gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
+static gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
{
gentity_t *spot;
vec3_t delta;
@@ -364,7 +175,7 @@ gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin,
if( SpotWouldTelefrag( spot ) )
continue;
- VectorSubtract( spot->s.origin, avoidPoint, delta );
+ VectorSubtract( spot->r.currentOrigin, avoidPoint, delta );
dist = VectorLength( delta );
for( i = 0; i < numSpots; i++ )
@@ -406,18 +217,18 @@ gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin,
if( !spot )
G_Error( "Couldn't find a spawn point" );
- VectorCopy( spot->s.origin, origin );
+ VectorCopy( spot->r.currentOrigin, origin );
origin[ 2 ] += 9;
- VectorCopy( spot->s.angles, angles );
+ VectorCopy( spot->r.currentAngles, angles );
return spot;
}
// select a random spot from the spawn points furthest away
rnd = random( ) * ( numSpots / 2 );
- VectorCopy( list_spot[ rnd ]->s.origin, origin );
+ VectorCopy( list_spot[ rnd ]->r.currentOrigin, origin );
origin[ 2 ] += 9;
- VectorCopy( list_spot[ rnd ]->s.angles, angles );
+ VectorCopy( list_spot[ rnd ]->r.currentAngles, angles );
return list_spot[ rnd ];
}
@@ -425,102 +236,45 @@ gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin,
/*
================
-G_SelectAlienSpawnPoint
-
-go to a random point that doesn't telefrag
-================
-*/
-gentity_t *G_SelectAlienSpawnPoint( vec3_t preference )
-{
- gentity_t *spot;
- int count;
- gentity_t *spots[ MAX_SPAWN_POINTS ];
-
- if( level.numAlienSpawns <= 0 )
- return NULL;
-
- count = 0;
- spot = NULL;
-
- while( ( spot = G_Find( spot, FOFS( classname ),
- BG_FindEntityNameForBuildable( BA_A_SPAWN ) ) ) != NULL )
- {
- if( !spot->spawned )
- continue;
-
- if( spot->health <= 0 )
- continue;
-
- if( !spot->s.groundEntityNum )
- continue;
-
- if( spot->clientSpawnTime > 0 )
- continue;
-
- if( G_CheckSpawnPoint( spot->s.number, spot->s.origin,
- spot->s.origin2, BA_A_SPAWN, NULL ) != NULL )
- continue;
-
- spots[ count ] = spot;
- count++;
- }
-
- if( !count )
- return NULL;
-
- return G_ClosestEnt( preference, spots, count );
-}
-
-
-/*
-================
-G_SelectHumanSpawnPoint
+G_SelectSpawnBuildable
-go to a random point that doesn't telefrag
+find the nearest buildable of the right type that is
+spawned/healthy/unblocked etc.
================
*/
-gentity_t *G_SelectHumanSpawnPoint( vec3_t preference )
+static gentity_t *G_SelectSpawnBuildable( vec3_t preference, buildable_t buildable )
{
- gentity_t *spot;
- int count;
- gentity_t *spots[ MAX_SPAWN_POINTS ];
-
- if( level.numHumanSpawns <= 0 )
- return NULL;
+ gentity_t *search, *spot;
- count = 0;
- spot = NULL;
+ search = spot = NULL;
- while( ( spot = G_Find( spot, FOFS( classname ),
- BG_FindEntityNameForBuildable( BA_H_SPAWN ) ) ) != NULL )
+ while( ( search = G_Find( search, FOFS( classname ),
+ BG_Buildable( buildable )->entityName ) ) != NULL )
{
- if( !spot->spawned )
+ if( !search->spawned )
continue;
- if( spot->health <= 0 )
+ if( search->health <= 0 )
continue;
- if( !spot->s.groundEntityNum )
+ if( search->s.groundEntityNum == ENTITYNUM_NONE )
continue;
- if( spot->clientSpawnTime > 0 )
+ if( search->clientSpawnTime > 0 )
continue;
- if( G_CheckSpawnPoint( spot->s.number, spot->s.origin,
- spot->s.origin2, BA_H_SPAWN, NULL ) != NULL )
+ if( G_CheckSpawnPoint( search->s.number, search->r.currentOrigin,
+ search->s.origin2, buildable, NULL ) != NULL )
continue;
- spots[ count ] = spot;
- count++;
+ if( !spot || DistanceSquared( preference, search->r.currentOrigin ) <
+ DistanceSquared( preference, spot->r.currentOrigin ) )
+ spot = search;
}
- if( !count )
- return NULL;
-
- return G_ClosestEnt( preference, spots, count );
+ return spot;
}
-
/*
===========
G_SelectSpawnPoint
@@ -541,25 +295,35 @@ G_SelectTremulousSpawnPoint
Chooses a player start, deathmatch start, etc
============
*/
-gentity_t *G_SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t origin, vec3_t angles )
+gentity_t *G_SelectTremulousSpawnPoint( team_t team, vec3_t preference, vec3_t origin, vec3_t angles )
{
gentity_t *spot = NULL;
- if( team == PTE_ALIENS )
- spot = G_SelectAlienSpawnPoint( preference );
- else if( team == PTE_HUMANS )
- spot = G_SelectHumanSpawnPoint( preference );
+ if( team == TEAM_ALIENS )
+ {
+ if( level.numAlienSpawns <= 0 )
+ return NULL;
+
+ spot = G_SelectSpawnBuildable( preference, BA_A_SPAWN );
+ }
+ else if( team == TEAM_HUMANS )
+ {
+ if( level.numHumanSpawns <= 0 )
+ return NULL;
+
+ spot = G_SelectSpawnBuildable( preference, BA_H_SPAWN );
+ }
//no available spots
if( !spot )
return NULL;
- if( team == PTE_ALIENS )
- G_CheckSpawnPoint( spot->s.number, spot->s.origin, spot->s.origin2, BA_A_SPAWN, origin );
- else if( team == PTE_HUMANS )
- G_CheckSpawnPoint( spot->s.number, spot->s.origin, spot->s.origin2, BA_H_SPAWN, origin );
+ if( team == TEAM_ALIENS )
+ G_CheckSpawnPoint( spot->s.number, spot->r.currentOrigin, spot->s.origin2, BA_A_SPAWN, origin );
+ else if( team == TEAM_HUMANS )
+ G_CheckSpawnPoint( spot->s.number, spot->r.currentOrigin, spot->s.origin2, BA_H_SPAWN, origin );
- VectorCopy( spot->s.angles, angles );
+ VectorCopy( spot->r.currentAngles, angles );
angles[ ROLL ] = 0;
return spot;
@@ -569,42 +333,11 @@ gentity_t *G_SelectTremulousSpawnPoint( pTeam_t team, vec3_t preference, vec3_t
/*
===========
-G_SelectInitialSpawnPoint
-
-Try to find a spawn point marked 'initial', otherwise
-use normal spawn selection.
-============
-*/
-gentity_t *G_SelectInitialSpawnPoint( vec3_t origin, vec3_t angles )
-{
- gentity_t *spot;
-
- spot = NULL;
- while( ( spot = G_Find( spot, FOFS( classname ), "info_player_deathmatch" ) ) != NULL )
- {
- if( spot->spawnflags & 1 )
- break;
- }
-
- if( !spot || SpotWouldTelefrag( spot ) )
- {
- return G_SelectSpawnPoint( vec3_origin, origin, angles );
- }
-
- VectorCopy( spot->s.origin, origin );
- origin[ 2 ] += 9;
- VectorCopy( spot->s.angles, angles );
-
- return spot;
-}
-
-/*
-===========
G_SelectSpectatorSpawnPoint
============
*/
-gentity_t *G_SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
+static gentity_t *G_SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
{
FindIntermissionPoint( );
@@ -633,8 +366,8 @@ gentity_t *G_SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
if( !spot )
return G_SelectSpectatorSpawnPoint( origin, angles );
- VectorCopy( spot->s.origin, origin );
- VectorCopy( spot->s.angles, angles );
+ VectorCopy( spot->r.currentOrigin, origin );
+ VectorCopy( spot->r.currentAngles, angles );
return spot;
}
@@ -658,8 +391,8 @@ gentity_t *G_SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles )
if( !spot )
return G_SelectSpectatorSpawnPoint( origin, angles );
- VectorCopy( spot->s.origin, origin );
- VectorCopy( spot->s.angles, angles );
+ VectorCopy( spot->r.currentOrigin, origin );
+ VectorCopy( spot->r.currentAngles, angles );
return spot;
}
@@ -681,7 +414,7 @@ BodySink
After sitting around for five seconds, fall into the ground and dissapear
=============
*/
-void BodySink( gentity_t *ent )
+static void BodySink( gentity_t *ent )
{
//run on first BodySink call
if( !ent->active )
@@ -706,40 +439,17 @@ void BodySink( gentity_t *ent )
/*
=============
-BodyFree
-
-After sitting around for a while the body becomes a freebie
-=============
-*/
-void BodyFree( gentity_t *ent )
-{
- ent->killedBy = -1;
-
- //if not claimed in the next minute destroy
- ent->think = BodySink;
- ent->nextthink = level.time + 60000;
-}
-
-
-/*
-=============
SpawnCorpse
A player is respawning, so make an entity that looks
just like the existing corpse to leave behind.
=============
*/
-void SpawnCorpse( gentity_t *ent )
+static void SpawnCorpse( gentity_t *ent )
{
gentity_t *body;
int contents;
- vec3_t origin, dest;
- trace_t tr;
- float vDiff;
-
- // prevent crashing everyone with bad corpsenum bug
- if( ent->client->pers.connected != CON_CONNECTED )
- return;
+ vec3_t origin, mins;
VectorCopy( ent->r.currentOrigin, origin );
@@ -752,17 +462,18 @@ void SpawnCorpse( gentity_t *ent )
body = G_Spawn( );
- VectorCopy( ent->s.apos.trBase, body->s.angles );
+ VectorCopy( ent->s.apos.trBase, body->s.apos.trBase );
+ VectorCopy( ent->s.apos.trBase, body->r.currentAngles );
body->s.eFlags = EF_DEAD;
body->s.eType = ET_CORPSE;
- body->s.number = body - g_entities;
body->timestamp = level.time;
body->s.event = 0;
body->r.contents = CONTENTS_CORPSE;
- body->s.clientNum = ent->client->ps.stats[ STAT_PCLASS ];
+ body->clipmask = MASK_DEADSOLID;
+ body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ];
body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL;
- if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
body->classname = "humanCorpse";
else
body->classname = "alienCorpse";
@@ -815,25 +526,19 @@ void SpawnCorpse( gentity_t *ent )
body->takedamage = qfalse;
- body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ];
- ent->health = 0;
+ body->health = ent->health;
//change body dimensions
- BG_FindBBoxForClass( ent->client->ps.stats[ STAT_PCLASS ], NULL, NULL, NULL, body->r.mins, body->r.maxs );
- vDiff = body->r.mins[ 2 ] - ent->r.mins[ 2 ];
+ BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], mins, NULL, NULL, body->r.mins, body->r.maxs );
//drop down to match the *model* origins of ent and body
- VectorSet( dest, origin[ 0 ], origin[ 1 ], origin[ 2 ] - vDiff );
- trap_Trace( &tr, origin, body->r.mins, body->r.maxs, dest, body->s.number, body->clipmask );
- VectorCopy( tr.endpos, origin );
+ origin[2] += mins[ 2 ] - body->r.mins[ 2 ];
G_SetOrigin( body, origin );
- VectorCopy( origin, body->s.origin );
body->s.pos.trType = TR_GRAVITY;
body->s.pos.trTime = level.time;
VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );
- VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
trap_LinkEntity( body );
}
@@ -846,7 +551,7 @@ G_SetClientViewAngle
==================
*/
-void G_SetClientViewAngle( gentity_t *ent, vec3_t angle )
+void G_SetClientViewAngle( gentity_t *ent, const vec3_t angle )
{
int i;
@@ -859,8 +564,9 @@ void G_SetClientViewAngle( gentity_t *ent, vec3_t angle )
ent->client->ps.delta_angles[ i ] = cmdAngle - ent->client->pers.cmd.angles[ i ];
}
- VectorCopy( angle, ent->s.angles );
- VectorCopy( ent->s.angles, ent->client->ps.viewangles );
+ VectorCopy( angle, ent->s.apos.trBase );
+ VectorCopy( angle, ent->r.currentAngles );
+ VectorCopy( angle, ent->client->ps.viewangles );
}
/*
@@ -870,52 +576,79 @@ respawn
*/
void respawn( gentity_t *ent )
{
+ int i;
+
SpawnCorpse( ent );
- //TA: Clients can't respawn - they must go thru the class cmd
+ // Clients can't respawn - they must go through the class cmd
ent->client->pers.classSelection = PCL_NONE;
ClientSpawn( ent, NULL, NULL, NULL );
-}
-/*
-================
-TeamCount
+ // stop any following clients that don't have sticky spec on
+ for( i = 0; i < level.maxclients; i++ )
+ {
+ if( level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW &&
+ level.clients[ i ].sess.spectatorClient == ent - g_entities )
+ {
+ if( !( level.clients[ i ].pers.stickySpec ) )
+ {
+ if( !G_FollowNewClient( &g_entities[ i ], 1 ) )
+ G_StopFollowing( &g_entities[ i ] );
+ }
+ else
+ G_FollowLockView( &g_entities[ i ] );
+ }
+ }
+}
-Returns number of players on a team
-================
-*/
-team_t TeamCount( int ignoreClientNum, int team )
+static qboolean G_IsEmoticon( const char *s, qboolean *escaped )
{
- int i;
- int count = 0;
+ int i, j;
+ const char *p = s;
+ char emoticon[ MAX_EMOTICON_NAME_LEN ] = {""};
+ qboolean escape = qfalse;
- for( i = 0 ; i < level.maxclients ; i++ )
+ if( *p != '[' )
+ return qfalse;
+ p++;
+ if( *p == '[' )
{
- if( i == ignoreClientNum )
- continue;
-
- if( level.clients[ i ].pers.connected == CON_DISCONNECTED )
- continue;
-
- if( level.clients[ i ].sess.sessionTeam == team )
- count++;
+ escape = qtrue;
+ p++;
}
-
- return count;
+ i = 0;
+ while( *p && i < ( MAX_EMOTICON_NAME_LEN - 1 ) )
+ {
+ if( *p == ']' )
+ {
+ for( j = 0; j < level.emoticonCount; j++ )
+ {
+ if( !Q_stricmp( emoticon, level.emoticons[ j ].name ) )
+ {
+ *escaped = escape;
+ return qtrue;
+ }
+ }
+ return qfalse;
+ }
+ emoticon[ i++ ] = *p;
+ emoticon[ i ] = '\0';
+ p++;
+ }
+ return qfalse;
}
-
/*
===========
-ClientCleanName
+G_ClientCleanName
============
*/
-static void ClientCleanName( const char *in, char *out, int outSize, qboolean special )
+static void G_ClientCleanName( const char *in, char *out, int outSize )
{
int len, colorlessLen;
- char ch;
char *p;
int spaces;
+ qboolean escaped;
qboolean invalid = qfalse;
//save room for trailing null byte
@@ -927,44 +660,48 @@ static void ClientCleanName( const char *in, char *out, int outSize, qboolean sp
*p = 0;
spaces = 0;
- while( 1 )
+ for( ; *in; in++ )
{
- ch = *in++;
- if( !ch )
- break;
-
// don't allow leading spaces
- if( !*p && ch == ' ' )
+ if( colorlessLen == 0 && *in == ' ' )
continue;
// don't allow nonprinting characters or (dead) console keys
- if( ch < ' ' || ch > '}' || ch == '`' || ch == '%' )
+ if( *in < ' ' || *in > '}' || *in == '`' )
continue;
// check colors
- if( Q_IsColorString( in - 1 ) )
+ if( Q_IsColorString( in ) )
{
+ in++;
+
// make sure room in dest for both chars
if( len > outSize - 2 )
break;
- *out++ = ch;
- len += 2;
+ *out++ = Q_COLOR_ESCAPE;
- // solo trailing carat is not a color prefix
- if( !*in ) {
- *out++ = COLOR_WHITE;
- break;
- }
+ *out++ = *in;
- *out++ = *in;
+ len += 2;
+ continue;
+ }
+ else if( !g_emoticonsAllowedInNames.integer && G_IsEmoticon( in, &escaped ) )
+ {
+ // make sure room in dest for both chars
+ if( len > outSize - 2 )
+ break;
- in++;
+ *out++ = '[';
+ *out++ = '[';
+ len += 2;
+ if( escaped )
+ in++;
continue;
}
// don't allow too many consecutive spaces
- if( ch == ' ' )
+ if( *in == ' ' )
{
spaces++;
if( spaces > 3 )
@@ -976,7 +713,7 @@ static void ClientCleanName( const char *in, char *out, int outSize, qboolean sp
if( len > outSize - 1 )
break;
- *out++ = ch;
+ *out++ = *in;
colorlessLen++;
len++;
}
@@ -984,7 +721,7 @@ static void ClientCleanName( const char *in, char *out, int outSize, qboolean sp
*out = 0;
// don't allow names beginning with "[skipnotify]" because it messes up /ignore-related code
- if( !Q_strncmp( p, "[skipnotify]", 12 ) )
+ if( !Q_stricmpn( p, "[skipnotify]", 12 ) )
invalid = qtrue;
// don't allow comment-beginning strings because it messes up various parsers
@@ -1002,37 +739,6 @@ static void ClientCleanName( const char *in, char *out, int outSize, qboolean sp
/*
-===================
-G_NextNewbieName
-
-Generate a unique, known-good name for an UnnamedPlayer
-===================
-*/
-char *G_NextNewbieName( gentity_t *ent )
-{
- char newname[ MAX_NAME_LENGTH ];
- char namePrefix[ MAX_NAME_LENGTH - 4 ];
- char err[ MAX_STRING_CHARS ];
-
- if( g_newbieNamePrefix.string[ 0 ] )
- Q_strncpyz( namePrefix, g_newbieNamePrefix.string , sizeof( namePrefix ) );
- else
- strcpy( namePrefix, "Newbie#" );
-
- while( level.numNewbies < 10000 )
- {
- strcpy( newname, va( "%s%i", namePrefix, level.numNewbies ) );
- if ( G_admin_name_check( ent, newname, err, sizeof( err ) ) )
- {
- return va( "%s", newname );
- }
- level.numNewbies++; // Only increments if the last requested name was used.
- }
- return "UnnamedPlayer";
-}
-
-
-/*
======================
G_NonSegModel
@@ -1099,24 +805,19 @@ The game can override any of the settings and call trap_SetUserinfo
if desired.
============
*/
-void ClientUserinfoChanged( int clientNum, qboolean forceName )
+char *ClientUserinfoChanged( int clientNum, qboolean forceName )
{
gentity_t *ent;
- int teamTask, teamLeader, health;
- char *s;
- char model[ MAX_QPATH ];
- char buffer[ MAX_QPATH ];
+ char *s, *s2;
+ char model[ MAX_QPATH] = { '\0' };
+ char buffer[ MAX_QPATH ] = { '\0' };
char filename[ MAX_QPATH ];
char oldname[ MAX_NAME_LENGTH ];
char newname[ MAX_NAME_LENGTH ];
char err[ MAX_STRING_CHARS ];
qboolean revertName = qfalse;
- qboolean showRenameMsg = qtrue;
gclient_t *client;
- char c1[ MAX_INFO_STRING ];
- char c2[ MAX_INFO_STRING ];
char userinfo[ MAX_INFO_STRING ];
- pTeam_t team;
ent = g_entities + clientNum;
client = ent->client;
@@ -1130,81 +831,49 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
"disconnect \"illegal or malformed userinfo\n\"" );
trap_DropClient( ent - g_entities,
"dropped: illegal or malformed userinfo");
+ return "Illegal or malformed userinfo";
}
+ // If their userinfo overflowed, tremded is in the process of disconnecting them.
+ // If we send our own disconnect, it won't work, so just return to prevent crashes later
+ // in this function. This check must come after the Info_Validate call.
+ else if( !userinfo[ 0 ] )
+ return "Empty (overflowed) userinfo";
-
- // check for local client
- s = Info_ValueForKey( userinfo, "ip" );
-
- if( !strcmp( s, "localhost" ) )
- client->pers.localClient = qtrue;
-
- // check the item prediction
- s = Info_ValueForKey( userinfo, "cg_predictItems" );
-
- if( !atoi( s ) )
- client->pers.predictItemPickup = qfalse;
- else
- client->pers.predictItemPickup = qtrue;
+ // stickyspec toggle
+ s = Info_ValueForKey( userinfo, "cg_stickySpec" );
+ client->pers.stickySpec = atoi( s ) != 0;
// set name
Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );
s = Info_ValueForKey( userinfo, "name" );
-
- if ( !G_admin_permission( ent, ADMF_SPECIALNAME ) )
- ClientCleanName( s, newname, sizeof( newname ), qfalse );
- else
- ClientCleanName( s, newname, sizeof( newname ), qtrue );
+ G_ClientCleanName( s, newname, sizeof( newname ) );
if( strcmp( oldname, newname ) )
{
- if( !strlen( oldname ) && client->pers.connected != CON_CONNECTED )
- showRenameMsg = qfalse;
-
- // in case we need to revert and there's no oldname
- if ( !G_admin_permission( ent, ADMF_SPECIALNAME ) )
- ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ), qfalse );
- else
- ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ), qtrue );
-
- if( g_newbieNumbering.integer )
+ if( !forceName && client->pers.namelog->nameChangeTime &&
+ level.time - client->pers.namelog->nameChangeTime <=
+ g_minNameChangePeriod.value * 1000 )
{
- if( !strcmp( newname, "UnnamedPlayer" ) )
- Q_strncpyz( newname, G_NextNewbieName( ent ), sizeof( newname ) );
- if( !strcmp( oldname, "UnnamedPlayer" ) )
- Q_strncpyz( oldname, G_NextNewbieName( ent ), sizeof( oldname ) );
+ trap_SendServerCommand( ent - g_entities, va(
+ "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"",
+ g_minNameChangePeriod.integer ) );
+ revertName = qtrue;
}
-
-
- if( !forceName )
+ else if( !forceName && g_maxNameChanges.integer > 0 &&
+ client->pers.namelog->nameChanges >= g_maxNameChanges.integer )
{
- if( G_IsMuted( client ) )
- {
- trap_SendServerCommand( ent - g_entities,
- "print \"You cannot change your name while you are muted\n\"" );
- revertName = qtrue;
- }
- else if( client->pers.nameChangeTime &&
- ( level.time - client->pers.nameChangeTime )
- <= ( g_minNameChangePeriod.value * 1000 ) )
- {
- trap_SendServerCommand( ent - g_entities, va(
- "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"",
- g_minNameChangePeriod.integer ) );
- revertName = qtrue;
- }
- else if( g_maxNameChanges.integer > 0
- && client->pers.nameChanges >= g_maxNameChanges.integer
- && !G_admin_permission( ent, ADMF_SPECIAL ) )
- {
- trap_SendServerCommand( ent - g_entities, va(
- "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"",
- g_maxNameChanges.integer ) );
- revertName = qtrue;
- }
+ trap_SendServerCommand( ent - g_entities, va(
+ "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"",
+ g_maxNameChanges.integer ) );
+ revertName = qtrue;
}
-
- if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) )
+ else if( !forceName && client->pers.namelog->muted )
+ {
+ trap_SendServerCommand( ent - g_entities,
+ "print \"You cannot change your name while you are muted\n\"" );
+ revertName = qtrue;
+ }
+ else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) )
{
trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) );
revertName = qtrue;
@@ -1212,110 +881,96 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
if( revertName )
{
- Q_strncpyz( client->pers.netname, oldname,
+ Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer",
sizeof( client->pers.netname ) );
Info_SetValueForKey( userinfo, "name", oldname );
trap_SetUserinfo( clientNum, userinfo );
}
else
{
- Q_strncpyz( client->pers.netname, newname,
- sizeof( client->pers.netname ) );
- Info_SetValueForKey( userinfo, "name", newname );
- trap_SetUserinfo( clientNum, userinfo );
- if( client->pers.connected == CON_CONNECTED )
+ G_CensorString( client->pers.netname, newname,
+ sizeof( client->pers.netname ), ent );
+ if( !forceName && client->pers.connected == CON_CONNECTED )
+ {
+ client->pers.namelog->nameChangeTime = level.time;
+ client->pers.namelog->nameChanges++;
+ }
+ if( *oldname )
{
- client->pers.nameChangeTime = level.time;
- client->pers.nameChanges++;
+ G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%c%s%c^7\"\n",
+ clientNum, client->pers.ip.str, client->pers.guid,
+ oldname, client->pers.netname,
+ DECOLOR_OFF, client->pers.netname, DECOLOR_ON );
}
}
+ G_namelog_update_name( client );
}
- if( client->sess.sessionTeam == TEAM_SPECTATOR )
+ if ( client->pers.teamSelection == TEAM_HUMANS )
{
- if( client->sess.spectatorState == SPECTATOR_SCOREBOARD )
- Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) );
- }
+ int i;
+ qboolean found = qfalse;
- if( client->pers.connected >= CON_CONNECTING && showRenameMsg )
- {
- if( strcmp( oldname, client->pers.netname ) )
- {
- //dont show if players invisible
- if( client->sess.invisible != qtrue )
- trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE
- " renamed to %s^7\n\"", oldname, client->pers.netname ) );
- if( g_decolourLogfiles.integer)
- {
- char decoloured[ MAX_STRING_CHARS ] = "";
- if( g_decolourLogfiles.integer == 1 )
- {
- Com_sprintf( decoloured, sizeof(decoloured), " (\"%s^7\" -> \"%s^7\")", oldname, client->pers.netname );
- G_DecolorString( decoloured, decoloured );
- G_LogPrintfColoured( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"%s\n", clientNum,
- client->pers.ip, client->pers.guid, oldname, client->pers.netname, decoloured );
- }
- else
- {
- G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"%s\n", clientNum,
- client->pers.ip, client->pers.guid, oldname, client->pers.netname, decoloured );
- }
+ s = Info_ValueForKey(userinfo, "model");
- }
- else
+ for ( i = 0; i < level.playerModelCount; i++ )
+ {
+ if ( !strcmp(s, level.playerModel[i]) )
{
- G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"\n", clientNum,
- client->pers.ip, client->pers.guid, oldname, client->pers.netname );
+ found = qtrue;
+ break;
}
- G_admin_namelog_update( client, qfalse );
}
- }
- // set max health
- health = atoi( Info_ValueForKey( userinfo, "handicap" ) );
- client->pers.maxHealth = health;
+ if ( !found )
+ s = NULL;
+ else if ( !g_cheats.integer
+ && !forceName
+ && !G_admin_permission( ent, va("MODEL%s", s) ) )
+ s = NULL;
- if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 )
- client->pers.maxHealth = 100;
-
- //hack to force a client update if the config string does not change between spawning
- if( client->pers.classSelection == PCL_NONE )
- client->pers.maxHealth = 0;
-
- // set model
- if( client->ps.stats[ STAT_PCLASS ] == PCL_HUMAN && BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) )
+ if (s)
+ {
+ s2 = Info_ValueForKey(userinfo, "skin");
+ s2 = GetSkin(s, s2);
+ }
+ }
+ else
{
- Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( PCL_HUMAN_BSUIT ),
- BG_FindSkinNameForClass( PCL_HUMAN_BSUIT ) );
+ s = NULL;
}
- else if( client->pers.classSelection == PCL_NONE )
+
+ if( client->pers.classSelection == PCL_NONE )
{
//This looks hacky and frankly it is. The clientInfo string needs to hold different
//model details to that of the spawning class or the info change will not be
//registered and an axis appears instead of the player model. There is zero chance
//the player can spawn with the battlesuit, hence this choice.
- Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( PCL_HUMAN_BSUIT ),
- BG_FindSkinNameForClass( PCL_HUMAN_BSUIT ) );
+ Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName,
+ BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName );
}
else
{
- Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( client->pers.classSelection ),
- BG_FindSkinNameForClass( client->pers.classSelection ) );
- }
- Q_strncpyz( model, buffer, sizeof( model ) );
+ if ( !(client->pers.classSelection == PCL_HUMAN_BSUIT) && s )
+ {
+ Com_sprintf( buffer, MAX_QPATH, "%s/%s", s, s2 );
+ }
+ else
+ {
+ Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName,
+ BG_ClassConfig( client->pers.classSelection )->skinName );
+ }
- //don't bother setting model type if spectating
- if( client->pers.classSelection != PCL_NONE )
- {
//model segmentation
Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg",
- BG_FindModelNameForClass( client->pers.classSelection ) );
+ BG_ClassConfig( client->pers.classSelection )->modelName );
if( G_NonSegModel( filename ) )
client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL;
else
client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL;
}
+ Q_strncpyz( model, buffer, sizeof( model ) );
// wallwalk follow
s = Info_ValueForKey( userinfo, "cg_wwFollow" );
@@ -1333,13 +988,44 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
else
client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE;
+ // always sprint
+ s = Info_ValueForKey( userinfo, "cg_sprintToggle" );
+
+ if( atoi( s ) )
+ client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE;
+ else
+ client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE;
+
+ // fly speed
+ s = Info_ValueForKey( userinfo, "cg_flySpeed" );
+
+ if( *s )
+ client->pers.flySpeed = atoi( s );
+ else
+ client->pers.flySpeed = BG_Class( PCL_NONE )->speed;
+
+ // disable blueprint errors
+ s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" );
+
+ if( atoi( s ) )
+ client->pers.disableBlueprintErrors = qtrue;
+ else
+ client->pers.disableBlueprintErrors = qfalse;
+
+ client->pers.buildableRangeMarkerMask =
+ atoi( Info_ValueForKey( userinfo, "cg_buildableRangeMarkerMask" ) );
+
// teamInfo
s = Info_ValueForKey( userinfo, "teamoverlay" );
- if( ! *s || atoi( s ) != 0 )
- client->pers.teamInfo = qtrue;
+ if( atoi( s ) != 0 )
+ {
+ // teamoverlay was enabled so we need an update
+ if( client->pers.teamInfo == 0 )
+ client->pers.teamInfo = 1;
+ }
else
- client->pers.teamInfo = qfalse;
+ client->pers.teamInfo = 0;
s = Info_ValueForKey( userinfo, "cg_unlagged" );
if( !s[0] || atoi( s ) != 0 )
@@ -1347,67 +1033,26 @@ void ClientUserinfoChanged( int clientNum, qboolean forceName )
else
client->pers.useUnlagged = qfalse;
- // team task (0 = none, 1 = offence, 2 = defence)
- teamTask = atoi( Info_ValueForKey( userinfo, "teamtask" ) );
- // team Leader (1 = leader, 0 is normal player)
- teamLeader = client->sess.teamLeader;
-
- // colors
- strcpy( c1, Info_ValueForKey( userinfo, "color1" ) );
- strcpy( c2, Info_ValueForKey( userinfo, "color2" ) );
-
- team = client->pers.teamSelection;
+ Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ),
+ sizeof( client->pers.voice ) );
// send over a subset of the userinfo keys so other clients can
// print scoreboards, display models, and play custom sounds
- if ( client->sess.invisible != qtrue )
- {
- Com_sprintf( userinfo, sizeof( userinfo ),
- "n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\"
- "hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\"
- "tl\\%d\\ig\\%16s",
- client->pers.netname, team, model, model, c1, c2,
- client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask,
- teamLeader, BG_ClientListString( &client->sess.ignoreList ) );
-
- trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );
- } else {
- trap_SetConfigstring( CS_PLAYERS + clientNum, "" );
- }
+
+ Com_sprintf( userinfo, sizeof( userinfo ),
+ "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s",
+ client->pers.netname, client->pers.teamSelection, model,
+ Com_ClientListString( &client->sess.ignoreList ),
+ client->pers.voice );
+
+ trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );
+
/*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/
-}
-/*
-===========
-LogAutobahn
-===========
-*/
-void G_LogAutobahn(gentity_t *ent, const char *userinfo, int rating,
- qboolean onConnect)
-{
- char ip_buffer[20];
- const char *ip, *name, *verb;
-
- verb = (onConnect ? "refused" : "dropped");
-
- if (userinfo) {
- Q_strncpyz(ip_buffer, Info_ValueForKey(userinfo, "ip"),
- sizeof(ip_buffer));
- ip = ip_buffer;
- name = Info_ValueForKey(userinfo, "name");
- } else {
- ip = ent->client->pers.ip;
- name = ent->client->pers.netname;
- }
-
- G_LogPrintf("Autobahn: %s %i %s %+i \"%s^7\"\n", verb, ent - g_entities,
- ip, rating, name);
-
- if (g_adminAutobahnNotify.integer)
- G_AdminsPrintf("Autobahn %s '%s^7' with rating %+i, connecting from %s.\n",
- verb, name, rating, ip);
+ return NULL;
}
+
/*
===========
ClientConnect
@@ -1428,52 +1073,59 @@ to the server machine, but qfalse on map changes and tournement
restarts.
============
*/
-char *ClientConnect( int clientNum, qboolean firstTime )
+const char *ClientConnect( int clientNum, qboolean firstTime )
{
char *value;
+ char *userInfoError;
gclient_t *client;
char userinfo[ MAX_INFO_STRING ];
gentity_t *ent;
- char guid[ 33 ];
- char ip[ 16 ] = {""};
char reason[ MAX_STRING_CHARS ] = {""};
int i;
ent = &g_entities[ clientNum ];
+ client = &level.clients[ clientNum ];
+
+ // ignore if client already connected
+ if( client->pers.connected != CON_DISCONNECTED )
+ return NULL;
+
+ ent->client = client;
+ memset( client, 0, sizeof( *client ) );
trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
value = Info_ValueForKey( userinfo, "cl_guid" );
- Q_strncpyz( guid, value, sizeof( guid ) );
-
- // check for admin ban
- if( G_admin_ban_check( userinfo, reason, sizeof( reason ) ) )
- {
- return va( "%s", reason );
- }
+ Q_strncpyz( client->pers.guid, value, sizeof( client->pers.guid ) );
- // IP filtering
- // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=500
- // recommanding PB based IP / GUID banning, the builtin system is pretty limited
- // check to see if they are on the banned IP list
value = Info_ValueForKey( userinfo, "ip" );
- i = 0;
- while( *value && i < sizeof( ip ) - 2 )
+ // check for local client
+ if( !strcmp( value, "localhost" ) )
+ client->pers.localClient = qtrue;
+ G_AddressParse( value, &client->pers.ip );
+
+ client->pers.admin = G_admin_admin( client->pers.guid );
+
+ client->pers.alternateProtocol = trap_Cvar_VariableIntegerValue( va( "sv_clAltProto%i", clientNum ) );
+
+ if( client->pers.alternateProtocol == 2 && client->pers.guid[ 0 ] == '\0' )
{
- if( *value != '.' && ( *value < '0' || *value > '9' ) )
- break;
- ip[ i++ ] = *value;
- value++;
+ size_t len = strlen( client->pers.ip.str );
+ if( len == 0 )
+ len = 1;
+ for( i = 0; i < sizeof( client->pers.guid ) - 1; ++i )
+ {
+ int j = client->pers.ip.str[ i % len ] + rand() / ( RAND_MAX / 16 + 1 );
+ client->pers.guid[ i ] = "0123456789ABCDEF"[ j % 16 ];
+ }
+ client->pers.guid[ sizeof( client->pers.guid ) - 1 ] = '\0';
+ client->pers.guidless = qtrue;
}
- ip[ i ] = '\0';
- if( G_FilterPacket( value ) )
- return "You are banned from this server.";
- if( strlen( ip ) < 7 && strcmp( Info_ValueForKey( userinfo, "ip" ), "localhost" ) )
+ // check for admin ban
+ if( G_admin_ban_check( ent, reason, sizeof( reason ) ) )
{
- G_AdminsPrintf( "Connect from client with invalid IP: '%s' NAME: '%s^7'\n",
- ip, Info_ValueForKey( userinfo, "name" ) );
- return "Invalid client data";
+ return va( "%s", reason );
}
// check for a password
@@ -1483,66 +1135,12 @@ char *ClientConnect( int clientNum, qboolean firstTime )
strcmp( g_password.string, value ) != 0 )
return "Invalid password";
- schachtmeisterJudgement_t *smj = NULL;
-
- if (!(G_admin_permission_guid(guid, ADMF_NOAUTOBAHN)
- || G_admin_permission_guid(guid, ADMF_IMMUNITY)))
- {
- extern g_admin_namelog_t *g_admin_namelog[128];
- for (i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[i]; ++i)
- {
- if (!Q_stricmp(g_admin_namelog[i]->ip, ip)
- || !Q_stricmp(g_admin_namelog[i]->guid, guid))
- {
- schachtmeisterJudgement_t *j = &g_admin_namelog[i]->smj;
- if (j->ratingTime)
- {
- if (j->rating >= g_schachtmeisterClearThreshold.integer)
- break;
- else if (j->rating <= g_schachtmeisterAutobahnThreshold.integer)
- {
- G_LogAutobahn( ent, userinfo, j->rating, qtrue );
- return g_schachtmeisterAutobahnMessage.string;
- }
- smj = j;
- }
- break;
- }
- }
- }
-
- // they can connect
- ent->client = level.clients + clientNum;
- client = ent->client;
-
- memset( client, 0, sizeof(*client) );
-
// add guid to session so we don't have to keep parsing userinfo everywhere
- if( !guid[ 0 ] )
- {
- Q_strncpyz( client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
- sizeof( client->pers.guid ) );
- }
- else
- {
- Q_strncpyz( client->pers.guid, guid, sizeof( client->pers.guid ) );
- }
+ for( i = 0; i < sizeof( client->pers.guid ) - 1 &&
+ isxdigit( client->pers.guid[ i ] ); i++ );
- Q_strncpyz( client->pers.ip, ip, sizeof( client->pers.ip ) );
- client->pers.adminLevel = G_admin_level( ent );
-
- // do autoghost now so that there won't be any name conflicts later on
- if ( g_autoGhost.integer && client->pers.guid[ 0 ] != 'X' )
- {
- for ( i = 0; i < MAX_CLIENTS; i++ )
- {
- if ( i != ent - g_entities && g_entities[i].client && g_entities[i].client->pers.connected != CON_DISCONNECTED && !Q_stricmp( g_entities[i].client->pers.guid, client->pers.guid ) )
- {
- trap_SendServerCommand( i, "disconnect \"You may not be connected to this server multiple times\"" );
- trap_DropClient( i, "disconnected" );
- }
- }
- }
+ if( i < sizeof( client->pers.guid ) - 1 )
+ return "Invalid GUID";
client->pers.connected = CON_CONNECTING;
@@ -1552,81 +1150,36 @@ char *ClientConnect( int clientNum, qboolean firstTime )
G_ReadSessionData( client );
- if( firstTime )
- client->pers.firstConnect = qtrue;
- else
- client->pers.firstConnect = qfalse;
-
// get and distribute relevent paramters
- ClientUserinfoChanged( clientNum, qfalse );
-
- G_admin_set_adminname( ent );
-
- if( g_decolourLogfiles.integer )
- {
- char decoloured[ MAX_STRING_CHARS ] = "";
- if( g_decolourLogfiles.integer == 1 )
- {
- Com_sprintf( decoloured, sizeof(decoloured), " (\"%s^7\")", client->pers.netname );
- G_DecolorString( decoloured, decoloured );
- G_LogPrintfColoured( "ClientConnect: %i [%s] (%s) \"%s^7\"%s\n", clientNum,
- client->pers.ip, client->pers.guid, client->pers.netname, decoloured );
- }
- else
- {
- G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\"%s\n", clientNum,
- client->pers.ip, client->pers.guid, client->pers.netname, decoloured );
- }
- }
- else
- {
- G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\"\n", clientNum,
- client->pers.ip, client->pers.guid, client->pers.netname );
- }
-
- if( client->pers.adminLevel )
- {
- G_LogPrintf( "ClientAuth: %i [%s] \"%s^7\" authenticated to admin level %i using GUID %s (^7%s)\n", clientNum, client->pers.ip, client->pers.netname, client->pers.adminLevel, client->pers.guid, client->pers.adminName );
- }
+ G_namelog_connect( client );
+ userInfoError = ClientUserinfoChanged( clientNum, qfalse );
+ if( userInfoError != NULL )
+ return userInfoError;
+
+ G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%c%s%c^7\"\n",
+ clientNum, client->pers.ip.str, client->pers.guid,
+ client->pers.netname,
+ DECOLOR_OFF, client->pers.netname, DECOLOR_ON );
// don't do the "xxx connected" messages if they were caried over from previous level
- if( client->sess.invisible != qtrue )
- {
- if( firstTime )
- trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"", client->pers.netname ) );
+ if( firstTime )
+ trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"",
+ client->pers.netname ) );
- // count current clients and rank for scoreboard
- CalculateRanks( );
- G_admin_namelog_update( client, qfalse );
- }
+ if( client->pers.admin )
+ G_admin_authlog( ent );
+
+ // count current clients and rank for scoreboard
+ CalculateRanks( );
+
// if this is after !restart keepteams or !restart switchteams, apply said selection
- if ( client->sess.restartTeam != PTE_NONE ) {
+ if ( client->sess.restartTeam != TEAM_NONE )
+ {
G_ChangeTeam( ent, client->sess.restartTeam );
- client->sess.restartTeam = PTE_NONE;
+ client->sess.restartTeam = TEAM_NONE;
}
- if( !( G_admin_permission( ent, ADMF_NOAUTOBAHN ) ||
- G_admin_permission( ent, ADMF_IMMUNITY ) ) )
- {
- extern g_admin_namelog_t *g_admin_namelog[ 128 ];
- for( i = 0; i < MAX_ADMIN_NAMELOGS && g_admin_namelog[ i ]; i++ )
- {
- if( !Q_stricmp( ip, g_admin_namelog[ i ]->ip ) || !Q_stricmp( guid, g_admin_namelog[ i ]->guid ) )
- {
- schachtmeisterJudgement_t *j = &g_admin_namelog[i]->smj;
- if( j->ratingTime )
- {
- if( j->rating >= g_schachtmeisterClearThreshold.integer )
- break;
- else if( j->rating <= g_schachtmeisterAutobahnThreshold.integer )
- return g_schachtmeisterAutobahnMessage.string;
- G_AdminsPrintf( "%s^7 (#%d) has rating %d\n", ent->client->pers.netname, ent - g_entities, j->rating );
- }
- break;
- }
- }
- }
return NULL;
}
@@ -1635,9 +1188,9 @@ char *ClientConnect( int clientNum, qboolean firstTime )
===========
ClientBegin
-called when a client has finished connecting, and is ready
-to be placed into the level. This will happen every level load,
-and on transition between teams, but doesn't happen on respawns
+Called when a client has finished connecting, and is ready
+to be placed into the level. This will happen on every
+level load and level restart, but doesn't happen on respawns.
============
*/
void ClientBegin( int clientNum )
@@ -1650,6 +1203,10 @@ void ClientBegin( int clientNum )
client = level.clients + clientNum;
+ // ignore if client already entered the game
+ if( client->pers.connected != CON_CONNECTING )
+ return;
+
if( ent->r.linked )
trap_UnlinkEntity( ent );
@@ -1660,8 +1217,6 @@ void ClientBegin( int clientNum )
client->pers.connected = CON_CONNECTED;
client->pers.enterTime = level.time;
- client->pers.teamState.state = TEAM_BEGIN;
- client->pers.classSelection = PCL_NONE;
// save eflags around this, because changing teams will
// cause this to happen with a valid entity, and we
@@ -1674,44 +1229,19 @@ void ClientBegin( int clientNum )
client->ps.eFlags = flags;
// locate ent at a spawn point
-
ClientSpawn( ent, NULL, NULL, NULL );
- // Ignore invisible players for this section:
- if ( client->sess.invisible != qtrue )
- {
- trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname ) );
-
- // auto denybuild
- if( G_admin_permission( ent, ADMF_NO_BUILD ) )
- client->pers.denyBuild = qtrue;
-
- // auto mute flag
- if( G_admin_permission( ent, ADMF_NO_CHAT ) )
- client->pers.muted = qtrue;
+ trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname ) );
- // name can change between ClientConnect() and ClientBegin()
- G_admin_namelog_update( client, qfalse );
+ G_namelog_restore( client );
- if( g_scrimMode.integer == 1 )
- {
- ADMP( "^5Scrim mode is enabled. Teams are locked and you can only use spectator chat.\n" );
- }
-
- // request the clients PTR code
- trap_SendServerCommand( ent - g_entities, "ptrcrequest" );
- }
G_LogPrintf( "ClientBegin: %i\n", clientNum );
- if( !Q_stricmp( ent->client->pers.guid, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ) &&
- g_outdatedClientMessage.string[0] )
- {
- trap_SendServerCommand( client->ps.clientNum, va(
- "print \"%s\n\"", g_outdatedClientMessage.string ) );
- }
-
// count current clients and rank for scoreboard
CalculateRanks( );
+
+ // send the client a list of commands that can be used
+ G_ListCommands( ent );
}
/*
@@ -1723,7 +1253,7 @@ after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
-void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles )
+void ClientSpawn( gentity_t *ent, gentity_t *spawn, const vec3_t origin, const vec3_t angles )
{
int index;
vec3_t spawn_origin, spawn_angles;
@@ -1731,8 +1261,8 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
int i;
clientPersistant_t saved;
clientSession_t savedSess;
+ qboolean savedNoclip, savedCliprcontents;
int persistant[ MAX_PERSISTANT ];
- gentity_t *spawnPoint = NULL;
int flags;
int savedPing;
int teamLocal;
@@ -1742,75 +1272,65 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
int maxAmmo, maxClips;
weapon_t weapon;
-
index = ent - g_entities;
client = ent->client;
teamLocal = client->pers.teamSelection;
- //TA: only start client if chosen a class and joined a team
- if( client->pers.classSelection == PCL_NONE && teamLocal == PTE_NONE )
+ //if client is dead and following teammate, stop following before spawning
+ if( client->sess.spectatorClient != -1 )
{
- client->sess.sessionTeam = TEAM_SPECTATOR;
+ client->sess.spectatorClient = -1;
client->sess.spectatorState = SPECTATOR_FREE;
}
+
+ // only start client if chosen a class and joined a team
+ if( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE )
+ client->sess.spectatorState = SPECTATOR_FREE;
else if( client->pers.classSelection == PCL_NONE )
- {
- client->sess.sessionTeam = TEAM_SPECTATOR;
client->sess.spectatorState = SPECTATOR_LOCKED;
- }
-
- //if client is dead and following teammate, stop following before spawning
- if(ent->client->sess.spectatorClient!=-1)
- {
- ent->client->sess.spectatorClient = -1;
- ent->client->sess.spectatorState = SPECTATOR_FREE;
- }
-
- if( origin != NULL )
- VectorCopy( origin, spawn_origin );
- if( angles != NULL )
- VectorCopy( angles, spawn_angles );
+ // if client is dead and following teammate, stop following before spawning
+ if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
+ G_StopFollowing( ent );
// find a spawn point
// do it before setting health back up, so farthest
// ranging doesn't count this client
- if( client->sess.sessionTeam == TEAM_SPECTATOR )
+ if( client->sess.spectatorState != SPECTATOR_NOT )
{
- if( teamLocal == PTE_NONE )
- spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
- else if( teamLocal == PTE_ALIENS )
- spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
- else if( teamLocal == PTE_HUMANS )
- spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
+ if( teamLocal == TEAM_ALIENS )
+ spawn = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
+ else if( teamLocal == TEAM_HUMANS )
+ spawn = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
+ else
+ spawn = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
}
else
{
- if( spawn == NULL )
+ if( origin == NULL || angles == NULL )
{
- G_Error( "ClientSpawn: spawn is NULL\n" );
+ G_Error( "ClientSpawn: origin or angles is NULL" );
return;
}
- spawnPoint = spawn;
+ VectorCopy( origin, spawn_origin );
+ VectorCopy( angles, spawn_angles );
- if( ent != spawn )
+ if( spawn != NULL && spawn != ent )
{
//start spawn animation on spawnPoint
- G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue );
+ G_SetBuildableAnim( spawn, BANIM_SPAWN1, qtrue );
- if( spawnPoint->biteam == PTE_ALIENS )
- spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME;
- else if( spawnPoint->biteam == PTE_HUMANS )
- spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME;
+ if( spawn->buildableTeam == TEAM_ALIENS )
+ spawn->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME;
+ else if( spawn->buildableTeam == TEAM_HUMANS )
+ spawn->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME;
}
}
- client->pers.teamState.state = TEAM_ACTIVE;
// toggle the teleport bit so the client knows to not lerp
- flags = ent->client->ps.eFlags & ( EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED );
- flags ^= EF_TELEPORT_BIT;
+ flags = ( ent->client->ps.eFlags & EF_TELEPORT_BIT ) ^ EF_TELEPORT_BIT;
G_UnlaggedClear( ent );
// clear everything but the persistant data
@@ -1818,6 +1338,8 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
saved = client->pers;
savedSess = client->sess;
savedPing = client->ps.ping;
+ savedNoclip = client->noclip;
+ savedCliprcontents = client->cliprcontents;
for( i = 0; i < MAX_PERSISTANT; i++ )
persistant[ i ] = client->ps.persistant[ i ];
@@ -1828,6 +1350,8 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
client->pers = saved;
client->sess = savedSess;
client->ps.ping = savedPing;
+ client->noclip = savedNoclip;
+ client->cliprcontents = savedCliprcontents;
client->lastkilled_client = -1;
for( i = 0; i < MAX_PERSISTANT; i++ )
@@ -1837,11 +1361,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
// increment the spawncount so the client will detect the respawn
client->ps.persistant[ PERS_SPAWN_COUNT ]++;
- client->ps.persistant[ PERS_TEAM ] = client->sess.sessionTeam;
-
- // restore really persistant things
- client->ps.persistant[ PERS_SCORE ] = client->pers.score;
- client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
+ client->ps.persistant[ PERS_SPECSTATE ] = client->sess.spectatorState;
client->airOutTime = level.time + 12000;
@@ -1853,52 +1373,58 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
ent->s.groundEntityNum = ENTITYNUM_NONE;
ent->client = &level.clients[ index ];
ent->takedamage = qtrue;
- ent->inuse = qtrue;
ent->classname = "player";
- ent->r.contents = CONTENTS_BODY;
- ent->clipmask = MASK_PLAYERSOLID;
+ if( client->noclip )
+ client->cliprcontents = CONTENTS_BODY;
+ else
+ ent->r.contents = CONTENTS_BODY;
+ if( client->pers.teamSelection == TEAM_NONE )
+ ent->clipmask = MASK_DEADSOLID;
+ else
+ ent->clipmask = MASK_PLAYERSOLID;
ent->die = player_die;
ent->waterlevel = 0;
ent->watertype = 0;
- ent->flags = 0;
+ ent->flags &= FL_GODMODE | FL_NOTARGET;
- //TA: calculate each client's acceleration
+ // calculate each client's acceleration
ent->evaluateAcceleration = qtrue;
- client->ps.stats[ STAT_WEAPONS ] = 0;
- client->ps.stats[ STAT_WEAPONS2 ] = 0;
- client->ps.stats[ STAT_SLOTS ] = 0;
+ client->ps.stats[ STAT_MISC ] = 0;
client->ps.eFlags = flags;
client->ps.clientNum = index;
- BG_FindBBoxForClass( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL );
+ BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL );
- if( client->sess.sessionTeam != TEAM_SPECTATOR )
- client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] =
- BG_FindHealthForClass( ent->client->pers.classSelection );
+ if( client->sess.spectatorState == SPECTATOR_NOT )
+ client->ps.stats[ STAT_MAX_HEALTH ] =
+ BG_Class( ent->client->pers.classSelection )->health;
else
- client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = 100;
+ client->ps.stats[ STAT_MAX_HEALTH ] = 100;
// clear entity values
if( ent->client->pers.classSelection == PCL_HUMAN )
{
- BG_AddWeaponToInventory( WP_BLASTER, client->ps.stats );
BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats );
weapon = client->pers.humanItemSelection;
}
- else if( client->sess.sessionTeam != TEAM_SPECTATOR )
- weapon = BG_FindStartWeaponForClass( ent->client->pers.classSelection );
+ else if( client->sess.spectatorState == SPECTATOR_NOT )
+ weapon = BG_Class( ent->client->pers.classSelection )->startWeapon;
else
weapon = WP_NONE;
- BG_FindAmmoForWeapon( weapon, &maxAmmo, &maxClips );
- BG_AddWeaponToInventory( weapon, client->ps.stats );
+ maxAmmo = BG_Weapon( weapon )->maxAmmo;
+ maxClips = BG_Weapon( weapon )->maxClips;
+ client->ps.stats[ STAT_WEAPON ] = weapon;
client->ps.ammo = maxAmmo;
client->ps.clips = maxClips;
- ent->client->ps.stats[ STAT_PCLASS ] = ent->client->pers.classSelection;
- ent->client->ps.stats[ STAT_PTEAM ] = ent->client->pers.teamSelection;
+ // We just spawned, not changing weapons
+ client->ps.persistant[ PERS_NEWWEAPON ] = 0;
+
+ ent->client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection;
+ ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection;
ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
ent->client->ps.stats[ STAT_STATE ] = 0;
@@ -1911,18 +1437,14 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
if( ent == spawn )
{
ent->health *= ent->client->pers.evolveHealthFraction;
- client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction;
+ client->ps.stats[ STAT_HEALTH ] = ent->health;
}
//clear the credits array
for( i = 0; i < MAX_CLIENTS; i++ )
ent->credits[ i ] = 0;
- client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA;
-
- if( mod_jetpackFuel.value >= 10.0f ) {
- client->jetpackfuel = mod_jetpackFuel.value;
- }
+ client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;
G_SetOrigin( ent, spawn_origin );
VectorCopy( spawn_origin, client->ps.origin );
@@ -1931,10 +1453,14 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
#define F_VEL 50.0f
//give aliens some spawn velocity
- if( client->sess.sessionTeam != TEAM_SPECTATOR &&
- client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
+ if( client->sess.spectatorState == SPECTATOR_NOT &&
+ client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
{
- if( ent == spawn )
+ if( spawn == NULL )
+ {
+ G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
+ }
+ else if( ent == spawn )
{
//evolution particle system
G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) );
@@ -1944,13 +1470,13 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
spawn_angles[ YAW ] += 180.0f;
AngleNormalize360( spawn_angles[ YAW ] );
- if( spawnPoint->s.origin2[ 2 ] > 0.0f )
+ if( spawn->s.origin2[ 2 ] > 0.0f )
{
vec3_t forward, dir;
AngleVectors( spawn_angles, forward, NULL, NULL );
VectorScale( forward, F_VEL, forward );
- VectorAdd( spawnPoint->s.origin2, forward, dir );
+ VectorAdd( spawn->s.origin2, forward, dir );
VectorNormalize( dir );
VectorScale( dir, UP_VEL, client->ps.velocity );
@@ -1959,11 +1485,14 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
}
}
- else if( client->sess.sessionTeam != TEAM_SPECTATOR &&
- client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
+ else if( client->sess.spectatorState == SPECTATOR_NOT &&
+ client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
{
- spawn_angles[ YAW ] += 180.0f;
- AngleNormalize360( spawn_angles[ YAW ] );
+ if( spawn != NULL )
+ {
+ spawn_angles[ YAW ] += 180.0f;
+ AngleNormalize360( spawn_angles[ YAW ] );
+ }
}
// the respawned flag will be cleared after the attack and jump keys come up
@@ -1972,13 +1501,14 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
G_SetClientViewAngle( ent, spawn_angles );
- if( !( client->sess.sessionTeam == TEAM_SPECTATOR ) )
+ if( client->sess.spectatorState == SPECTATOR_NOT )
{
- /*G_KillBox( ent );*/ //blame this if a newly spawned client gets stuck in another
trap_LinkEntity( ent );
// force the base weapon up
- client->ps.weapon = WP_NONE;
+ if( client->pers.teamSelection == TEAM_HUMANS )
+ G_ForceWeaponChange( ent, weapon );
+
client->ps.weaponstate = WEAPON_READY;
}
@@ -1987,8 +1517,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
client->ps.pm_time = 100;
client->respawnTime = level.time;
- if( g_gradualFreeFunds.integer < 2 )
- client->pers.lastFreekillTime = level.time;
+ ent->nextRegenTime = level.time;
client->inactivityTime = level.time + g_inactivity.integer * 1000;
client->latched_buttons = 0;
@@ -2002,21 +1531,10 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
else
{
// fire the targets of the spawn point
- if( !spawn )
- G_UseTargets( spawnPoint, ent );
+ if( spawn != NULL && spawn != ent )
+ G_UseTargets( spawn, ent );
- // select the highest weapon number available, after any
- // spawn given items have fired
- client->ps.weapon = 1;
-
- for( i = WP_NUM_WEAPONS - 1; i > 0 ; i-- )
- {
- if( BG_InventoryContainsWeapon( i, client->ps.stats ) )
- {
- client->ps.weapon = i;
- break;
- }
- }
+ client->ps.weapon = client->ps.stats[ STAT_WEAPON ];
}
// run a client frame to drop exactly to the floor,
@@ -2025,15 +1543,16 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
ent->client->pers.cmd.serverTime = level.time;
ClientThink( ent-g_entities );
+ VectorCopy( ent->client->ps.viewangles, ent->r.currentAngles );
+ VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
// positively link the client, even if the command times are weird
- if( client->sess.sessionTeam != TEAM_SPECTATOR )
+ if( client->sess.spectatorState == SPECTATOR_NOT )
{
BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
- VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
trap_LinkEntity( ent );
}
- //TA: must do this here so the number of active clients is calculated
+ // must do this here so the number of active clients is calculated
CalculateRanks( );
// run the presend to set anything else
@@ -2041,6 +1560,8 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles
// clear entity state values
BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
+
+ client->pers.infoChangeTime = level.time;
}
@@ -2061,55 +1582,40 @@ void ClientDisconnect( int clientNum )
gentity_t *ent;
gentity_t *tent;
int i;
- buildHistory_t *ptr;
ent = g_entities + clientNum;
- if( !ent->client )
+ if( !ent->client || ent->client->pers.connected == CON_DISCONNECTED )
return;
- // look through the bhist and readjust it if the referenced ent has left
- for( ptr = level.buildHistory; ptr; ptr = ptr->next )
- {
- if( ptr->ent == ent )
- {
- ptr->ent = NULL;
- Q_strncpyz( ptr->name, ent->client->pers.netname, MAX_NETNAME );
- }
- }
-
- if ( ent->client->sess.invisible != qtrue )
- G_admin_namelog_update( ent->client, qtrue );
G_LeaveTeam( ent );
+ G_namelog_disconnect( ent->client );
+ G_Vote( ent, TEAM_NONE, qfalse );
// stop any following clients
for( i = 0; i < level.maxclients; i++ )
{
// remove any /ignore settings for this clientNum
- BG_ClientListRemove( &level.clients[ i ].sess.ignoreList, clientNum );
+ Com_ClientListRemove( &level.clients[ i ].sess.ignoreList, clientNum );
}
// send effect if they were completely connected
if( ent->client->pers.connected == CON_CONNECTED &&
- ent->client->sess.sessionTeam != TEAM_SPECTATOR )
+ ent->client->sess.spectatorState == SPECTATOR_NOT )
{
tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
tent->s.clientNum = ent->s.clientNum;
}
- if( ent->client->pers.connection )
- ent->client->pers.connection->clientNum = -1;
-
- G_LogPrintf( "ClientDisconnect: %i [%s] (%s) \"%s\"\n", clientNum,
- ent->client->pers.ip, ent->client->pers.guid, ent->client->pers.netname );
+ G_LogPrintf( "ClientDisconnect: %i [%s] (%s) \"%s^7\"\n", clientNum,
+ ent->client->pers.ip.str, ent->client->pers.guid, ent->client->pers.netname );
trap_UnlinkEntity( ent );
- ent->s.modelindex = 0;
ent->inuse = qfalse;
ent->classname = "disconnected";
ent->client->pers.connected = CON_DISCONNECTED;
- ent->client->ps.persistant[ PERS_TEAM ] = TEAM_FREE;
- ent->client->sess.sessionTeam = TEAM_FREE;
+ ent->client->sess.spectatorState =
+ ent->client->ps.persistant[ PERS_SPECSTATE ] = SPECTATOR_NOT;
trap_SetConfigstring( CS_PLAYERS + clientNum, "");