/* * Cmd_Say_f */ void Cmd_Say_f( edict_t *ent, qboolean arg0, qboolean checkflood ) { char *p; char text[2048]; if( checkflood ) { if( CheckFlood( ent, qfalse ) ) return; } if( ent->r.client && ent->r.client->muted & 1 ) return; if( trap_Cmd_Argc() < 2 && !arg0 ) return; text[0] = 0; if( arg0 ) { Q_strncatz( text, trap_Cmd_Argv( 0 ), sizeof( text ) ); Q_strncatz( text, " ", sizeof( text ) ); Q_strncatz( text, trap_Cmd_Args(), sizeof( text ) ); } else { p = trap_Cmd_Args(); if( *p == '"' ) { if( p[strlen( p )-1] == '"' ) p[strlen( p )-1] = 0; p++; } Q_strncatz( text, p, sizeof( text ) ); } // don't let text be too long for malicious reasons if( strlen( text ) > 150 ) text[150] = 0; G_ChatMsg( NULL, ent, qfalse, "%s", text ); // racesow RS_ircSendMessage( va( "%s", COM_RemoveColorTokens(( ent->r.client->netname ) )), va( "%s", COM_RemoveColorTokens(( text)) ) ); // !racesow }
/* * G_SanitizeUserString */ static int G_SanitizeUserString( char *string, size_t size ) { static char *colorless = NULL; static size_t colorless_size = 0; int i, c_ascii; // life is hard, UTF-8 will have to go strip_highchars( string ); COM_SanitizeColorString( va( "%s", string ), string, size, -1, COLOR_WHITE ); Q_trim( string ); if( colorless_size < strlen( string ) + 1 ) { colorless_size = strlen( string ) + 1; G_Free( colorless ); colorless = ( char * )G_Malloc( colorless_size ); } Q_strncpyz( colorless, COM_RemoveColorTokens( string ), colorless_size ); // require at least one non-whitespace ascii char in the string // (this will upset people who would like to have a name entirely in a non-latin // script, but it makes damn sure you can't get an empty name by exploiting some // utf-8 decoder quirk) c_ascii = 0; for( i = 0; colorless[i]; i++ ) if( colorless[i] > 32 && colorless[i] < 127 ) c_ascii++; return c_ascii; }
/* * TV_Upstream_AutoRecordName */ static const char *TV_Upstream_AutoRecordName( upstream_t *upstream, char *name, size_t name_size ) { const char *gametype; char datetime[32]; char matchname[MAX_CONFIGSTRING_CHARS]; time_t long_time; struct tm *newtime; // date & time time( &long_time ); newtime = localtime( &long_time ); Q_snprintfz( datetime, sizeof( datetime ), "%04d-%02d-%02d_%02d-%02d", newtime->tm_year + 1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min ); Q_strncpyz( matchname, upstream->configstrings[CS_MATCHNAME], sizeof( matchname ) ); if( matchname[0] != '\0') { char *t = strstr( matchname, " vs " ); if( t ) memcpy( t, "_vs_", strlen( "_vs_" ) ); Q_strncpyz( matchname, COM_RemoveJunkChars( COM_RemoveColorTokens( matchname ) ), sizeof( matchname ) ); } // combine gametype = upstream->configstrings[CS_GAMETYPENAME]; Q_snprintfz( name, name_size, "%s_%s_%s%s%s_auto%04i", datetime, gametype, upstream->levelname, matchname[0] ? "_" : "", matchname, (int)brandom( 0, 9999 ) ); return name; }
void G_PlayerAward( edict_t *ent, const char *awardMsg ) { edict_t *other; char cmd[MAX_STRING_CHARS]; gameaward_t *ga; int i, size; score_stats_t *stats; if( !awardMsg || !awardMsg[0] || !ent->r.client ) return; Q_snprintfz( cmd, sizeof( cmd ), "aw \"%s\"", awardMsg ); trap_GameCmd( ent, cmd ); if( dedicated->integer ) G_Printf( "%s", COM_RemoveColorTokens( va( "%s receives a '%s' award.\n", ent->r.client->netname, awardMsg ) ) ); ent->r.client->level.stats.awards++; teamlist[ent->s.team].stats.awards++; G_Gametype_ScoreEvent( ent->r.client, "award", awardMsg ); stats = &ent->r.client->level.stats; if( !stats->awardAllocator ) stats->awardAllocator = LinearAllocator( sizeof( gameaward_t ), 0, _G_LevelMalloc, _G_LevelFree ); // ch : this doesnt work for race right? if( GS_MatchState() == MATCH_STATE_PLAYTIME || GS_MatchState() == MATCH_STATE_POSTMATCH ) { // ch : we store this locally to send to MM // first check if we already have this one on the clients list size = LA_Size( stats->awardAllocator ); ga = NULL; for( i = 0; i < size; i++ ) { ga = ( gameaward_t * )LA_Pointer( stats->awardAllocator, i ); if( !strncmp( ga->name, awardMsg, sizeof(ga->name)-1 ) ) break; } if( i >= size ) { ga = ( gameaward_t * )LA_Alloc( stats->awardAllocator ); memset( ga, 0, sizeof(*ga) ); ga->name = G_RegisterLevelString( awardMsg ); } if( ga ) ga->count++; } // add it to every player who's chasing this player for( other = game.edicts + 1; PLAYERNUM( other ) < gs.maxclients; other++ ) { if( !other->r.client || !other->r.inuse || !other->r.client->resp.chase.active ) continue; if( other->r.client->resp.chase.target == ENTNUM( ent ) ) trap_GameCmd( other, cmd ); } }
/* * TVM_ClientBegin * * called when a client has finished connecting, and is ready * to be placed into the game. This will happen every level load. */ void TVM_ClientBegin( tvm_relay_t *relay, edict_t *ent ) { edict_t *spot, *other; int i, specs; char hostname[MAX_CONFIGSTRING_CHARS]; assert( ent && ent->local && ent->r.client ); //TVM_Printf( "Begin: %s\n", ent->r.client->pers.netname ); ent->r.client->pers.connecting = false; spot = TVM_SelectSpawnPoint( ent ); if( spot ) { VectorCopy( spot->s.origin, ent->s.origin ); VectorCopy( spot->s.origin, ent->s.old_origin ); VectorCopy( spot->s.angles, ent->s.angles ); VectorCopy( spot->s.origin, ent->r.client->ps.pmove.origin ); VectorCopy( spot->s.angles, ent->r.client->ps.viewangles ); } else { VectorClear( ent->s.origin ); VectorClear( ent->s.old_origin ); VectorClear( ent->s.angles ); VectorClear( ent->r.client->ps.pmove.origin ); VectorClear( ent->r.client->ps.viewangles ); } ent->s.teleported = true; // set the delta angle for( i = 0; i < 3; i++ ) ent->r.client->ps.pmove.delta_angles[i] = ANGLE2SHORT( ent->s.angles[i] ) - ent->r.client->pers.cmd_angles[i]; specs = 0; for( i = 0; i < relay->local_maxclients; i++ ) { other = relay->local_edicts + i; if( other == ent ) continue; if( !other->r.inuse || !other->r.client ) continue; if( trap_GetClientState( relay, PLAYERNUM( other ) ) != CS_SPAWNED ) continue; specs++; } Q_strncpyz( hostname, relay->configStrings[CS_HOSTNAME], sizeof( hostname ) ); TVM_PrintMsg( relay, ent, S_COLOR_ORANGE "Welcome to %s! There %s currently %i spectator%s on this channel.\n", COM_RemoveColorTokens( hostname ), (specs == 1 ? "is" : "are"), specs, (specs == 1 ? "" : "s") ); TVM_PrintMsg( relay, ent, S_COLOR_ORANGE "For more information about chase camera modes type 'chase help' at console.\n" ); if( ent->r.client->chase.active ) TVM_ChaseClientEndSnapFrame( ent ); else TVM_ClientEndSnapFrame( ent ); }
static asstring_t *objectString_RemoveColorTokens( asstring_t *self ) { const char *s; if( !self->len ) return objectString_FactoryBuffer( NULL, 0 ); s = COM_RemoveColorTokens( self->buffer ); return objectString_FactoryBuffer( s, strlen(s) ); }
/* * ML_AddMap * Handles assigning memory for map and adding it to the list * in alphabetical order */ static void ML_AddMap( const char *filename, const char *fullname ) { mapinfo_t *map; char *buffer; char fullname_[MAX_CONFIGSTRING_CHARS]; if( !ML_ValidateFilename( filename ) ) return; if( !strcmp(filename, "ui") ) return; if( !fullname ) { ML_GetFullnameFromMap( filename, fullname_, sizeof( fullname_ ) ); fullname = fullname_; } if( !ML_ValidateFullname( fullname ) && *fullname ) // allow empty fullnames return; if( !strcmp(fullname, "ui") ) return; ml_flush = true; // tell everyone that maplist has changed buffer = ( char* )Mem_ZoneMalloc( sizeof( mapinfo_t ) + strlen( filename ) + 1 + strlen( fullname ) + 1 ); map = ( mapinfo_t * )buffer; buffer += sizeof( mapinfo_t ); map->filename = buffer; strcpy( map->filename, filename ); COM_StripExtension( map->filename ); buffer += strlen( filename ) + 1; map->fullname = buffer; strcpy( map->fullname, fullname ); COM_RemoveColorTokens( map->fullname ); Q_strlwr( map->fullname ); Trie_Insert( mlist_filenames_trie, map->filename, map ); Trie_Insert( mlist_fullnames_trie, map->fullname, map ); map->next = maplist; maplist = map; }
/* * CG_SC_AutoRecordName */ static const char *CG_SC_AutoRecordName( void ) { time_t long_time; struct tm *newtime; static char name[MAX_STRING_CHARS]; char mapname[MAX_CONFIGSTRING_CHARS]; const char *cleanplayername, *cleanplayername2; // get date from system time( &long_time ); newtime = localtime( &long_time ); if( cg.view.POVent <= 0 ) { cleanplayername2 = ""; } else { // remove color tokens from player names (doh) cleanplayername = COM_RemoveColorTokens( cgs.clientInfo[cg.view.POVent-1].name ); // remove junk chars from player names for files cleanplayername2 = COM_RemoveJunkChars( cleanplayername ); } // lowercase mapname Q_strncpyz( mapname, cgs.configStrings[CS_MAPNAME], sizeof( mapname ) ); Q_strlwr( mapname ); // make file name // duel_year-month-day_hour-min_map_player Q_snprintfz( name, sizeof( name ), "%s_%04d-%02d-%02d_%02d-%02d_%s_%s_%04i", gs.gametypeName, newtime->tm_year + 1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, mapname, cleanplayername2, (int)brandom( 0, 9999 ) ); return name; }
/* * ML_GetFullnameFromMap * Get fullname of map from file or worldspawn (slow) */ static void ML_GetFullnameFromMap( const char *filename, char *fullname, size_t len ) { char *buffer; *fullname = '\0'; // Try and load fullname from a file FS_LoadFile( va( "maps/%s.txt", filename ), ( void ** )&buffer, NULL, 0 ); if( buffer ) { char *line = buffer; Q_strncpyz( fullname, COM_Parse( &line ), len ); FS_FreeFile( buffer ); return; } // Try and load fullname from worldspawn CM_LoadMapMessage( va( "maps/%s.bsp", filename ), fullname, len ); COM_RemoveColorTokens( fullname ); }
void G_PlayerAward( edict_t *ent, const char *awardMsg ) { edict_t *other, *third; if( !awardMsg || !awardMsg[0] || !ent->r.client ) return; trap_GameCmd( ent, va( "aw \"%s\"", awardMsg ) ); if( dedicated->integer ) G_Printf( "%s", COM_RemoveColorTokens( va( "%s receives a '%s' award.\n", ent->r.client->netname, awardMsg ) ) ); ent->r.client->level.stats.awards++; teamlist[ent->s.team].stats.awards++; G_Gametype_ScoreEvent( ent->r.client, "award", awardMsg ); // add it to every player who's chasing this player for( other = game.edicts + 1; PLAYERNUM( other ) < gs.maxclients; other++ ) { if( !other->r.client || !other->r.inuse || !other->r.client->resp.chase.active ) continue; if( other->r.client->resp.chase.target == ent->s.number ) { trap_GameCmd( other, va( "aw \"%s\"", awardMsg ) ); // someone could also be chase-caming the guy in the chasecam for( third = game.edicts + 1; PLAYERNUM( third ) < gs.maxclients; third++ ) { if( !third->r.client || !third->r.inuse || !third->r.client->resp.chase.active ) continue; if( third->r.client->resp.chase.target == other->s.number ) trap_GameCmd( third, va( "aw \"%s\"", awardMsg ) ); } } } }
/* * TV_Downstream_FixName * * Make name valid, so it's not used by anyone else or so. See G_SetName * Client can be given, so conflict with that client's name won't matter * The returned value will be overwritten by the next call to this function */ char *TV_Downstream_FixName( const char *original_name, client_t *client ) { const char *invalid_prefixes[] = { "console", "[team]", "[spec]", "[bot]", "[coach]", "[tv]", NULL }; client_t *other; static char name[MAX_NAME_BYTES]; char colorless[MAX_NAME_BYTES]; int i, trynum, trylen; int c_ascii; int maxchars; // we allow NULL to be passed for name if( !original_name ) original_name = ""; Q_strncpyz( name, original_name, sizeof( name ) ); // life is hard, UTF-8 will have to go strip_highchars( name ); COM_SanitizeColorString( va( "%s", name ), name, sizeof( name ), -1, COLOR_WHITE ); // remove leading whitespace while( name[0] == ' ' ) memmove( name, name + 1, strlen( name ) ); // remove trailing whitespace while( strlen( name ) && name[strlen(name)-1] == ' ' ) name[strlen(name)-1] = '\0'; Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); maxchars = MAX_NAME_CHARS; if( client && client->tv ) maxchars = min( maxchars + 10, MAX_NAME_BYTES-1 ); // require at least one non-whitespace ascii char in the name // (this will upset people who would like to have a name entirely in a non-latin // script, but it makes damn sure you can't get an empty name by exploiting some // utf-8 decoder quirk) c_ascii = 0; for( i = 0; colorless[i]; i++ ) if( colorless[i] > 32 && colorless[i] < 127 ) c_ascii++; if( !c_ascii ) { Q_strncpyz( name, "Player", sizeof( name ) ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); } for( i = 0; invalid_prefixes[i] != NULL; i++ ) { if( !Q_strnicmp( colorless, invalid_prefixes[i], strlen( invalid_prefixes[i] ) ) ) { Q_strncpyz( name, "Player", sizeof( name ) ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); } } trynum = 1; do { for( i = 0, other = tvs.clients; i < tv_maxclients->integer; i++, other++ ) { if( ( client && other == client ) || other->state == CS_FREE || other->state == CS_ZOMBIE ) continue; // if nick is already in use, try with (number) appended if( !Q_stricmp( colorless, COM_RemoveColorTokens( other->name ) ) ) { if( trynum != 1 ) // remove last try name[strlen( name ) - strlen( va( "%s(%i)", S_COLOR_WHITE, trynum-1 ) )] = 0; // make sure there is enough space for the postfix trylen = strlen( va( "%s(%i)", S_COLOR_WHITE, trynum ) ); if( (int)strlen( colorless ) + trylen > maxchars ) { COM_SanitizeColorString( va( "%s", name ), name, sizeof( name ), maxchars - trylen, COLOR_WHITE ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); } // add the postfix Q_strncatz( name, va( "%s(%i)", S_COLOR_WHITE, trynum ), sizeof( name ) ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); // go trough all clients again trynum++; break; } } } while( i != tv_maxclients->integer && trynum <= MAX_CLIENTS ); return name; }
/* * G_ChasePlayer */ void G_ChasePlayer( edict_t *ent, const char *name, bool teamonly, int followmode ) { int i; edict_t *e; gclient_t *client; int targetNum = -1; int oldTarget; bool can_follow = true; char colorlessname[MAX_NAME_BYTES]; client = ent->r.client; oldTarget = client->resp.chase.target; if( teamonly && !client->teamstate.is_coach ) can_follow = false; if( !can_follow && followmode ) { G_PrintMsg( ent, "Chasecam follow mode unavailable\n" ); followmode = false; } if( ent->r.client->resp.chase.followmode && !followmode ) G_PrintMsg( ent, "Disabling chasecam follow mode\n" ); // always disable chasing as a start memset( &client->resp.chase, 0, sizeof( chasecam_t ) ); // locate the requested target if( name && name[0] ) { // find it by player names for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !G_Chase_IsValidTarget( ent, e, teamonly ) ) continue; Q_strncpyz( colorlessname, COM_RemoveColorTokens( e->r.client->netname ), sizeof(colorlessname) ); if( !Q_stricmp( COM_RemoveColorTokens( name ), colorlessname ) ) { targetNum = PLAYERNUM( e ); break; } } // didn't find it by name, try by numbers if( targetNum == -1 ) { i = atoi( name ); if( i >= 0 && i < gs.maxclients ) { e = game.edicts + 1 + i; if( G_Chase_IsValidTarget( ent, e, teamonly ) ) targetNum = PLAYERNUM( e ); } } if( targetNum == -1 ) G_PrintMsg( ent, "Requested chasecam target is not available\n" ); } // try to reuse old target if we didn't find a valid one if( targetNum == -1 && oldTarget > 0 && oldTarget < gs.maxclients ) { e = game.edicts + 1 + oldTarget; if( G_Chase_IsValidTarget( ent, e, teamonly ) ) targetNum = PLAYERNUM( e ); } // if we still don't have a target, just pick the first valid one if( targetNum == -1 ) { for( e = game.edicts + 1; PLAYERNUM( e ) < gs.maxclients; e++ ) { if( !G_Chase_IsValidTarget( ent, e, teamonly ) ) continue; targetNum = PLAYERNUM( e ); break; } } // make the client a ghost G_GhostClient( ent ); if( targetNum != -1 ) { // we found a target, set up the chasecam client->resp.chase.target = targetNum + 1; client->resp.chase.teamonly = teamonly; client->resp.chase.followmode = followmode; G_Chase_SetChaseActive( ent, true ); } else { // stay as observer if( !teamonly ) ent->movetype = MOVETYPE_NOCLIP; client->level.showscores = false; G_Chase_SetChaseActive( ent, false ); G_CenterPrintMsg( ent, "No one to chase" ); } }
/** * Responds to a Steam server query. * * @param s query string * @param socket response socket * @param address response address * @param inmsg message for arguments * @return whether the request was handled as a Steam query */ bool SV_SteamServerQuery( const char *s, const socket_t *socket, const netadr_t *address, msg_t *inmsg ) { #if APP_STEAMID if( sv.state < ss_loading || sv.state > ss_game ) return false; // server not running if( ( !sv_public->integer && !NET_IsLANAddress( address ) ) || ( sv_maxclients->integer == 1 ) ) return false; if( !strcmp( s, "i" ) ) { // ping const char pingResponse[] = "j00000000000000"; Netchan_OutOfBand( socket, address, sizeof( pingResponse ), ( const uint8_t * )pingResponse ); return true; } if( !strcmp( s, "W" ) || !strcmp( s, "U\xFF\xFF\xFF\xFF" ) ) { // challenge - security feature, but since we don't send multiple packets always return 0 const uint8_t challengeResponse[] = { 'A', 0, 0, 0, 0 }; Netchan_OutOfBand( socket, address, sizeof( challengeResponse ), ( const uint8_t * )challengeResponse ); return true; } if( !strcmp( s, "TSource Engine Query" ) ) { // server info char hostname[MAX_INFO_VALUE]; char gamedir[MAX_QPATH]; char gamename[128]; char version[32]; char tags[MAX_STEAMQUERY_TAG_STRING]; int i, players = 0, bots = 0, maxclients = 0; int flags = 0x80 | 0x01; // game port | game ID containing app ID client_t *cl; msg_t msg; uint8_t msgbuf[MAX_STEAMQUERY_PACKETLEN - sizeof( int32_t )]; if( sv_showInfoQueries->integer ) Com_Printf( "Steam Info Packet %s\n", NET_AddressToString( address ) ); Q_strncpyz( hostname, COM_RemoveColorTokens( sv_hostname->string ), sizeof( hostname ) ); if( !hostname[0] ) Q_strncpyz( hostname, sv_hostname->dvalue, sizeof( hostname ) ); Q_strncpyz( gamedir, FS_GameDirectory(), sizeof( gamedir ) ); Q_strncpyz( gamename, APPLICATION, sizeof( gamename ) ); if( Cvar_Value( "g_instagib" ) ) Q_strncatz( gamename, " IG", sizeof( gamename ) ); if( sv.configstrings[CS_GAMETYPETITLE][0] || sv.configstrings[CS_GAMETYPENAME][0] ) { Q_strncatz( gamename, " ", sizeof( gamename ) ); Q_strncatz( gamename, sv.configstrings[sv.configstrings[CS_GAMETYPETITLE][0] ? CS_GAMETYPETITLE : CS_GAMETYPENAME], sizeof( gamename ) ); } for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state >= CS_CONNECTED ) { if( cl->tvclient ) // exclude TV from the max players count continue; if( cl->edict->r.svflags & SVF_FAKECLIENT ) bots++; players++; } maxclients++; } Q_snprintfz( version, sizeof( version ), "%i.%i.0.0", APP_VERSION_MAJOR, APP_VERSION_MINOR ); SV_GetSteamTags( tags ); if( tags[0] ) flags |= 0x20; MSG_Init( &msg, msgbuf, sizeof( msgbuf ) ); MSG_WriteByte( &msg, 'I' ); MSG_WriteByte( &msg, APP_PROTOCOL_VERSION ); MSG_WriteString( &msg, hostname ); MSG_WriteString( &msg, sv.mapname ); MSG_WriteString( &msg, gamedir ); MSG_WriteString( &msg, gamename ); MSG_WriteShort( &msg, 0 ); // app ID specified later MSG_WriteByte( &msg, min( players, 99 ) ); MSG_WriteByte( &msg, min( maxclients, 99 ) ); MSG_WriteByte( &msg, min( bots, 99 ) ); MSG_WriteByte( &msg, ( dedicated && dedicated->integer ) ? 'd' : 'l' ); MSG_WriteByte( &msg, STEAMQUERY_OS ); MSG_WriteByte( &msg, Cvar_String( "password" )[0] ? 1 : 0 ); MSG_WriteByte( &msg, 0 ); // VAC insecure MSG_WriteString( &msg, version ); MSG_WriteByte( &msg, flags ); // port MSG_WriteShort( &msg, sv_port->integer ); // tags if( flags & 0x20 ) MSG_WriteString( &msg, tags ); // 64-bit game ID - needed to specify app ID MSG_WriteLong( &msg, APP_STEAMID & 0xffffff ); MSG_WriteLong( &msg, 0 ); Netchan_OutOfBand( socket, address, msg.cursize, msg.data ); return true; } if( s[0] == 'U' ) { // players msg_t msg; uint8_t msgbuf[MAX_STEAMQUERY_PACKETLEN - sizeof( int32_t )]; int i, players = 0; client_t *cl; char name[MAX_NAME_BYTES]; unsigned int time = Sys_Milliseconds(); if( sv_showInfoQueries->integer ) Com_Printf( "Steam Players Packet %s\n", NET_AddressToString( address ) ); MSG_Init( &msg, msgbuf, sizeof( msgbuf ) ); MSG_WriteByte( &msg, 'D' ); MSG_WriteByte( &msg, 0 ); for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( ( cl->state < CS_CONNECTED ) || cl->tvclient ) continue; Q_strncpyz( name, COM_RemoveColorTokens( cl->name ), sizeof( name ) ); if( ( msg.cursize + 10 + strlen( name ) ) > sizeof( msgbuf ) ) break; MSG_WriteByte( &msg, i ); MSG_WriteString( &msg, name ); MSG_WriteLong( &msg, cl->edict->r.client->r.frags ); MSG_WriteFloat( &msg, ( float )( time - cl->lastconnect ) * 0.001f ); players++; if( players == 99 ) break; } msgbuf[1] = players; Netchan_OutOfBand( socket, address, msg.cursize, msg.data ); return true; } if( !strcmp( s, "s" ) ) { // master server query, terminated by \n, followed by the challenge int i; bool fromMaster = false; int challenge; char gamedir[MAX_QPATH], basedir[MAX_QPATH], tags[MAX_STEAMQUERY_TAG_STRING]; int players = 0, bots = 0, maxclients = 0; client_t *cl; char msg[MAX_STEAMQUERY_PACKETLEN]; for( i = 0; i < MAX_MASTERS; i++ ) { if( sv_masters[i].steam && NET_CompareAddress( address, &sv_masters[i].address ) ) { fromMaster = true; break; } } if( !fromMaster ) return true; if( sv_showInfoQueries->integer ) Com_Printf( "Steam Master Server Info Packet %s\n", NET_AddressToString( address ) ); challenge = MSG_ReadLong( inmsg ); Q_strncpyz( gamedir, FS_GameDirectory(), sizeof( gamedir ) ); Q_strncpyz( basedir, FS_BaseGameDirectory(), sizeof( basedir ) ); SV_GetSteamTags( tags ); for( i = 0; i < sv_maxclients->integer; i++ ) { cl = &svs.clients[i]; if( cl->state >= CS_CONNECTED ) { if( cl->tvclient ) // exclude TV from the max players count continue; if( cl->edict->r.svflags & SVF_FAKECLIENT ) bots++; players++; } maxclients++; } Q_snprintfz( msg, sizeof( msg ), "0\n\\protocol\\7\\challenge\\%i" // protocol must be 7 to match Source "\\players\\%i\\max\\%i\\bots\\%i" "\\gamedir\\%s\\map\\%s" "\\password\\%i\\os\\%c" "\\lan\\%i\\region\\255" "%s%s" "\\type\\%c\\secure\\0" "\\version\\%i.%i.0.0" "\\product\\%s\n", challenge, min( players, 99 ), min( maxclients, 99 ), min( bots, 99 ), gamedir, sv.mapname, Cvar_String( "password" )[0] ? 1 : 0, STEAMQUERY_OS, sv_public->integer ? 0 : 1, tags[0] ? "\\gametype\\" /* legacy - "gametype", not "tags" */ : "", tags, ( dedicated && dedicated->integer ) ? 'd' : 'l', APP_VERSION_MAJOR, APP_VERSION_MINOR, basedir ); NET_SendPacket( socket, ( const uint8_t * )msg, strlen( msg ), address ); return true; } if( s[0] == 'O' ) { // out of date message static bool printed = false; if( !printed ) { int i; for( i = 0; i < MAX_MASTERS; i++ ) { if( sv_masters[i].steam && NET_CompareAddress( address, &sv_masters[i].address ) ) { Com_Printf( "Server is out of date and cannot be added to the Steam master servers.\n" ); printed = true; return true; } } } return true; } #endif return false; }
/* * UI_DrawConnectScreen */ void UI_DrawConnectScreen( const char *serverName, const char *rejectmessage, int downloadType, const char *downloadFilename, float downloadPercent, int downloadSpeed, int connectCount, qboolean demoplaying, qboolean backGround ) { qboolean localhost, design = qfalse; char str[MAX_QPATH]; int x, y, xoffset, yoffset, width, height; unsigned int maxwidth; qboolean downloadFromWeb; char hostName[MAX_CONFIGSTRING_CHARS], mapname[MAX_CONFIGSTRING_CHARS], mapmessage[MAX_CONFIGSTRING_CHARS], gametype[MAX_CONFIGSTRING_CHARS], gametypeTitle[MAX_CONFIGSTRING_CHARS], gametypeVersion[MAX_CONFIGSTRING_CHARS], gametypeAuthor[MAX_CONFIGSTRING_CHARS], matchName[MAX_CONFIGSTRING_CHARS]; uis.demoplaying = demoplaying; //trap_S_StopBackgroundTrack(); localhost = (qboolean)( !serverName || !serverName[0] || !Q_stricmp( serverName, "localhost" ) ); trap_GetConfigString( CS_MAPNAME, mapname, sizeof( mapname ) ); trap_GetConfigString( CS_MESSAGE, mapmessage, sizeof( mapmessage ) ); if( backGround ) { Q_snprintfz( str, sizeof( str ), UI_SHADER_BACKGROUND, uis.backgroundNum ); trap_R_DrawStretchPic( 0, 0, uis.vidWidth, uis.vidHeight, 0, 0, 1, 1, colorWhite, trap_R_RegisterPic( str ) ); } // // not yet connected // x = 64; y = 64; xoffset = yoffset = 0; if( demoplaying ) Q_snprintfz( str, sizeof( str ), "Loading demo: %s", serverName ); else if( localhost ) Q_strncpyz( str, "Loading...", sizeof( str ) ); else if( mapname[0] ) Q_snprintfz( str, sizeof( str ), "Connecting to %s", serverName ); else Q_snprintfz( str, sizeof( str ), "Awaiting connection... %i", connectCount ); trap_SCR_DrawString( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, uis.fontSystemBig, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemBig ); if( design && !rejectmessage ) rejectmessage = "Connection was interrupted because the weather sux :P"; if( rejectmessage ) { x = uis.vidWidth / 2; y = uis.vidHeight / 3; height = trap_SCR_strHeight( uis.fontSystemMedium ) * 4; Q_strncpyz( str, "Refused: ", sizeof( str ) ); width = max( trap_SCR_strWidth( str, uis.fontSystemMedium, 0 ), trap_SCR_strWidth( rejectmessage, uis.fontSystemSmall, 0 ) ); width += 32 * 2; xoffset = UISCR_HorizontalAlignOffset( ALIGN_CENTER_MIDDLE, width ); yoffset = UISCR_VerticalAlignOffset( ALIGN_CENTER_MIDDLE, height ); UI_DrawBox( x + xoffset, y + yoffset, width, height, colorWarsowOrange, colorWhite, NULL, colorDkGrey ); yoffset += trap_SCR_strHeight( uis.fontSystemMedium ); xoffset += 32; trap_SCR_DrawString( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, uis.fontSystemMedium, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemMedium ); trap_SCR_DrawString( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, rejectmessage, uis.fontSystemSmall, colorBlack ); return; } if( mapname[0] ) { char levelshot[MAX_QPATH]; struct shader_s *levelshotShader; qboolean isDefaultlevelshot = qtrue; // // connected // x = 64; y = uis.vidHeight - 300; xoffset = yoffset = 0; width = uis.vidWidth; height = 200; UI_DrawBox( x + xoffset, y + yoffset, width, height, colorWarsowPurple, colorWhite, NULL, colorDkGrey ); xoffset += 16; yoffset += 16 + 4; maxwidth = uis.vidWidth - ( x + xoffset ); trap_GetConfigString( CS_HOSTNAME, hostName, sizeof( hostName ) ); trap_GetConfigString( CS_GAMETYPENAME, gametype, sizeof( gametype ) ); trap_GetConfigString( CS_GAMETYPETITLE, gametypeTitle, sizeof( gametypeTitle ) ); trap_GetConfigString( CS_GAMETYPEVERSION, gametypeVersion, sizeof( gametypeVersion ) ); trap_GetConfigString( CS_GAMETYPEAUTHOR, gametypeAuthor, sizeof( gametypeAuthor ) ); trap_GetConfigString( CS_MATCHNAME, matchName, sizeof( matchName ) ); Q_snprintfz( levelshot, sizeof( levelshot ), "levelshots/%s.jpg", mapname ); levelshotShader = trap_R_RegisterLevelshot( levelshot, uis.whiteShader, &isDefaultlevelshot ); if( !isDefaultlevelshot ) { int lw, lh, lx, ly; lh = height - 8; lw = lh * ( 4.0f/3.0f ); lx = uis.vidWidth - lw; ly = y + 4; trap_R_DrawStretchPic( lx, ly, lw, lh, 0, 0, 1, 1, colorWhite, levelshotShader ); } if( !localhost && !demoplaying ) { Q_snprintfz( str, sizeof( str ), "Server: %s", hostName ); trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemSmall ) + 8; } if( mapmessage[0] && Q_stricmp( mapname, mapmessage ) ) { Q_snprintfz( str, sizeof( str ), "Level: "S_COLOR_ORANGE"%s", COM_RemoveColorTokens( mapmessage ) ); trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemSmall ) + 8; } Q_snprintfz( str, sizeof( str ), "Map: %s", mapname ); trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemSmall ) + 8; if( matchName[0] ) { Q_snprintfz( str, sizeof( str ), "Match: %s", matchName ); trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemSmall ) + 8; } yoffset += trap_SCR_strHeight( uis.fontSystemSmall ) + 8; Q_snprintfz( str, sizeof( str ), "Gametype: "S_COLOR_ORANGE"%s", COM_RemoveColorTokens( gametypeTitle ) ); trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemSmall ) + 8; Q_snprintfz( str, sizeof( str ), "Version: %s", COM_RemoveColorTokens( gametypeVersion ) ); trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemSmall ) + 8; Q_snprintfz( str, sizeof( str ), "Author: %s", gametypeAuthor ); trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, str, maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemSmall ) + 8; } // downloading if( design && !downloadFilename ) { downloadFilename = "http://www.warsow.net/autoupdate/basewsw/map_wdm9a.pk3"; downloadType = 2; downloadPercent = 65.8; downloadSpeed = 325; } if( downloadType && downloadFilename ) { size_t len; const char *s; downloadFromWeb = ( downloadType == 2 ); x = uis.vidWidth / 2; y = uis.vidHeight / 3; width = 400; height = 128 - trap_SCR_strHeight( uis.fontSystemSmall ); if( uis.vidWidth <= width ) width = uis.vidWidth - 64; maxwidth = width - 48; xoffset = UISCR_HorizontalAlignOffset( ALIGN_CENTER_MIDDLE, width ); yoffset = UISCR_VerticalAlignOffset( ALIGN_CENTER_MIDDLE, height ); // adjust the box size for the extra number of lines needed to draw the file path s = downloadFilename; while( ( len = trap_SCR_StrlenForWidth( s, uis.fontSystemSmall, maxwidth ) ) > 0 ) { s += len; height += trap_SCR_strHeight( uis.fontSystemSmall ); } UI_DrawBox( x + xoffset, y + yoffset, width, height, colorWarsowPurple, colorWhite, NULL, colorDkGrey ); xoffset += 24; yoffset += 24; trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, downloadFromWeb ? "Downloading from web" : "Downloading from server", maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += trap_SCR_strHeight( uis.fontSystemSmall ); s = downloadFilename; while( ( len = trap_SCR_DrawStringWidth( x + xoffset, y + yoffset, ALIGN_LEFT_TOP, s, maxwidth, uis.fontSystemSmall, colorWhite ) ) > 0 ) { s += len; yoffset += trap_SCR_strHeight( uis.fontSystemSmall ); } yoffset += 16; UI_DrawPicBar( x + xoffset, y + yoffset, maxwidth, 24, ALIGN_LEFT_TOP, downloadPercent, trap_R_RegisterPic( "gfx/ui/progressbar" ),colorDkGrey, colorOrange ); Q_snprintfz( str, sizeof( str ), "%3.1f%c", downloadPercent, '%' ); trap_SCR_DrawStringWidth( x + xoffset + 12, y + yoffset + 12, ALIGN_LEFT_MIDDLE, str, maxwidth, uis.fontSystemSmall, colorWhite ); Q_snprintfz( str, sizeof( str ), "%ik/s", downloadSpeed ); trap_SCR_DrawStringWidth( x + xoffset + maxwidth - 12, y + yoffset + 12, ALIGN_RIGHT_MIDDLE, str, maxwidth, uis.fontSystemSmall, colorWhite ); yoffset += 24 + 8; } }
/* * SV_Status_f */ void SV_Status_f( void ) { int i, j, l; client_t *cl; const char *s; int ping; if( !svs.clients ) { Com_Printf( "No server running.\n" ); return; } Com_Printf( "map : %s\n", sv.mapname ); Com_Printf( "num score ping name lastmsg address port rate \n" ); Com_Printf( "--- ----- ---- --------------- ------- --------------------- ------ ------\n" ); for( i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++ ) { if( !cl->state ) continue; Com_Printf( "%3i ", i ); Com_Printf( "%5i ", cl->edict->r.client->r.frags ); if( cl->state == CS_CONNECTED ) Com_Printf( "CNCT " ); else if( cl->state == CS_ZOMBIE ) Com_Printf( "ZMBI " ); else if( cl->state == CS_CONNECTING ) Com_Printf( "AWAI " ); else { ping = cl->ping < 9999 ? cl->ping : 9999; Com_Printf( "%4i ", ping ); } s = COM_RemoveColorTokens( cl->name ); Com_Printf( "%s", s ); l = 16 - (int)strlen( s ); for( j = 0; j < l; j++ ) Com_Printf( " " ); Com_Printf( "%7i ", svs.realtime - cl->lastPacketReceivedTime ); s = NET_AddressToString( &cl->netchan.remoteAddress ); Com_Printf( "%s", s ); l = 22 - (int)strlen( s ); for( j = 0; j < l; j++ ) Com_Printf( " " ); Com_Printf( "%5i", cl->netchan.game_port ); #ifndef RATEKILLED // wsw : jal : print real rate in use Com_Printf( " " ); if( cl->edict && ( cl->edict->r.svflags & SVF_FAKECLIENT ) ) Com_Printf( "BOT" ); else if( cl->rate == 99999 ) Com_Printf( "LAN" ); else Com_Printf( "%5i", cl->rate ); #endif Com_Printf( " " ); if( cl->mv ) Com_Printf( "MV" ); Com_Printf( "\n" ); } Com_Printf( "\n" ); }
/* * G_MoveClientToTV * * Sends cmd to connect to a non-full TV server with round robin balancing */ void G_MoveClientToTV( edict_t *ent ) { int i; gclient_t *client, *best; static int last_tv = 0; bool isIPv6; char ip[MAX_INFO_VALUE]; int port; const char *p; if( !ent->r.client ) { return; } if( ent->r.svflags & SVF_FAKECLIENT ) { return; } if( ent->r.client->isTV ) { return; } best = NULL; for( i = 0; i < gs.maxclients; i++ ) { client = &game.clients[(last_tv + 1 + i) % gs.maxclients]; if( !client->isTV || client->connecting ) { // not a TV or not ready yet continue; } if( client->tv.numclients == client->tv.maxclients ) { // full continue; } if( !client->tv.channel ) { // invalid userinfo/channel number continue; } best = client; break; } if( !best ) { G_PrintMsg( ent, "Could not find a free TV server\n" ); return; } Q_strncpyz( ip, best->ip, sizeof( ip ) ); // check IP type p = strstr( ip, "::" ); isIPv6 = p != NULL; // strip port number from address string p = strrchr( ip, ':' ); if( p != NULL ) { ip[p - ip] = '\0'; } port = isIPv6 ? best->tv.port6 : best->tv.port; last_tv = best - game.clients; trap_GameCmd( ent, va( "memo tv_moveto \"%s\" %s:%hu#%i", COM_RemoveColorTokens( best->netname ), ip, port, best->tv.channel ) ); }
void CG_Democam_DrawCenterSubtitle( int y, unsigned int maxwidth, struct qfontface_s *font, char *text ) { char *ptr, *s, *t, c, d; int x = cgs.vidWidth / 2; if( !text || !text[0] ) { return; } int shadowOffset = 2 * cgs.vidHeight / 600; if( !shadowOffset ) { shadowOffset = 1; } if( !maxwidth || trap_SCR_strWidth( text, font, 0 ) <= maxwidth ) { trap_SCR_DrawStringWidth( x + shadowOffset, y + shadowOffset, ALIGN_CENTER_TOP, COM_RemoveColorTokens( text ), maxwidth, font, colorBlack ); trap_SCR_DrawStringWidth( x, y, ALIGN_CENTER_TOP, text, maxwidth, font, colorWhite ); return; } t = s = ptr = text; while( *s ) { while( *s && *s != ' ' && *s != '\n' ) s++; if( ( !*s || *s == '\n' ) && trap_SCR_strWidth( ptr, font, 0 ) < maxwidth ) { // new line or end of text, in both cases force write c = *s; *s = 0; trap_SCR_DrawStringWidth( x + shadowOffset, y + shadowOffset, ALIGN_CENTER_TOP, COM_RemoveColorTokens( ptr ), maxwidth, font, colorBlack ); trap_SCR_DrawStringWidth( x, y, ALIGN_CENTER_TOP, ptr, maxwidth, font, colorWhite ); *s = c; if( !*s ) { break; } t = s; s++; ptr = s; } else { c = *s; *s = 0; if( trap_SCR_strWidth( ptr, font, 0 ) < maxwidth ) { *s = c; t = s; s++; continue; } *s = c; d = *t; *t = 0; trap_SCR_DrawStringWidth( x + shadowOffset, y + shadowOffset, ALIGN_CENTER_TOP, COM_RemoveColorTokens( ptr ), maxwidth, font, colorBlack ); trap_SCR_DrawStringWidth( x, y, ALIGN_CENTER_TOP, ptr, maxwidth, font, colorWhite ); *t = d; s = t; s++; ptr = s; } y += trap_SCR_FontHeight( font ); } }
/** * Responds to a Steam server query. * * @param s query string * @param socket response socket * @param address response address * @param inmsg message for arguments * @return whether the request was handled as a Steam query */ bool TV_Downstream_SteamServerQuery( const char *s, const socket_t *socket, const netadr_t *address, msg_t *inmsg ) { #if APP_STEAMID if( ( !tv_public->integer && !NET_IsLANAddress( address ) ) || ( tv_maxclients->integer == 1 ) ) return false; if( !strcmp( s, "i" ) ) { // ping const char pingResponse[] = "j00000000000000"; Netchan_OutOfBand( socket, address, sizeof( pingResponse ), ( const uint8_t * )pingResponse ); return true; } if( !strcmp( s, "TSource Engine Query" ) ) { // server info char hostname[MAX_INFO_VALUE]; char gamedir[MAX_QPATH]; char version[32]; int i, count = 0; msg_t msg; uint8_t msgbuf[MAX_STEAMQUERY_PACKETLEN - sizeof( int32_t )]; Q_strncpyz( hostname, COM_RemoveColorTokens( tv_name->string ), sizeof( hostname ) ); if( !hostname[0] ) Q_strncpyz( hostname, tv_name->dvalue, sizeof( hostname ) ); Q_strncpyz( gamedir, FS_GameDirectory(), sizeof( gamedir ) ); for( i = 0; i < tv_maxclients->integer; i++ ) { if( tvs.clients[i].state >= CS_CONNECTED ) { count++; if( count == 99 ) break; } } Q_snprintfz( version, sizeof( version ), "%i.%i.0.0", APP_VERSION_MAJOR, APP_VERSION_MINOR ); MSG_Init( &msg, msgbuf, sizeof( msgbuf ) ); MSG_WriteByte( &msg, 'I' ); MSG_WriteByte( &msg, APP_PROTOCOL_VERSION ); MSG_WriteString( &msg, hostname ); MSG_WriteString( &msg, "" ); // no map MSG_WriteString( &msg, gamedir ); MSG_WriteString( &msg, APPLICATION " TV" ); MSG_WriteShort( &msg, 0 ); // app ID specified later MSG_WriteByte( &msg, count ); MSG_WriteByte( &msg, min( tv_maxclients->integer, 99 ) ); MSG_WriteByte( &msg, 0 ); // no bots MSG_WriteByte( &msg, 'p' ); MSG_WriteByte( &msg, STEAMQUERY_OS ); MSG_WriteByte( &msg, tv_password->string[0] ? 1 : 0 ); MSG_WriteByte( &msg, 0 ); // VAC insecure MSG_WriteString( &msg, version ); MSG_WriteByte( &msg, 0x40 | 0x1 ); // spectator data | game ID containing app ID // spectator data MSG_WriteShort( &msg, tv_port->integer ); MSG_WriteString( &msg, hostname ); // 64-bit game ID - needed to specify app ID MSG_WriteLong( &msg, APP_STEAMID & 0xffffff ); MSG_WriteLong( &msg, 0 ); Netchan_OutOfBand( socket, address, msg.cursize, msg.data ); return true; } if( !strcmp( s, "s" ) ) { // master server query, terminated by \n, followed by the challenge bool isSteamMaster = false; int challenge; char gamedir[MAX_QPATH], basedir[MAX_QPATH]; int i, count = 0; char msg[MAX_STEAMQUERY_PACKETLEN]; if( !TV_Downstream_IsMaster( address, &isSteamMaster ) || !isSteamMaster ) return true; challenge = MSG_ReadLong( inmsg ); Q_strncpyz( gamedir, FS_GameDirectory(), sizeof( gamedir ) ); Q_strncpyz( basedir, FS_BaseGameDirectory(), sizeof( basedir ) ); for( i = 0; i < tv_maxclients->integer; i++ ) { if( tvs.clients[i].state >= CS_CONNECTED ) { count++; if( count == 99 ) break; } } Q_snprintfz( msg, sizeof( msg ), "0\n\\protocol\\7\\challenge\\%i" // protocol must be 7 to match Source "\\players\\%i\\max\\%i\\bots\\0" "\\gamedir\\%s" "\\password\\%i\\os\\%c" "\\lan\\%i\\region\\255" "\\type\\p\\secure\\0" "\\version\\%i.%i.0.0" "\\product\\%s\n", challenge, count, min( tv_maxclients->integer, 99 ), gamedir, tv_password->string[0] ? 1 : 0, STEAMQUERY_OS, tv_public->integer ? 0 : 1, APP_VERSION_MAJOR, APP_VERSION_MINOR, basedir ); NET_SendPacket( socket, ( const uint8_t * )msg, strlen( msg ), address ); return true; } if( s[0] == 'O' ) { // out of date message static bool printed = false; if( !printed ) { bool isSteamMaster = false; if( TV_Downstream_IsMaster( address, &isSteamMaster ) && isSteamMaster ) { Com_Printf( "Server is out of date and cannot be added to the Steam master servers.\n" ); printed = true; } } return true; } #endif return false; }
/* * G_SetName */ static void G_SetName( edict_t *ent, const char *original_name ) { const char *invalid_prefixes[] = { "console", "[team]", "[spec]", "[bot]", "[coach]", "[tv]", NULL }; edict_t *other; char name[MAX_NAME_BYTES]; char colorless[MAX_NAME_BYTES]; int i, trynum, trylen; int c_ascii; int maxchars; if( !ent->r.client ) return; // we allow NULL to be passed for name if( !original_name ) original_name = ""; Q_strncpyz( name, original_name, sizeof( name ) ); c_ascii = G_SanitizeUserString( name, sizeof( name ) ); if( !c_ascii ) Q_strncpyz( name, "Player", sizeof( name ) ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); if( !( ent->r.svflags & SVF_FAKECLIENT ) ) { for( i = 0; invalid_prefixes[i] != NULL; i++ ) { if( !Q_strnicmp( colorless, invalid_prefixes[i], strlen( invalid_prefixes[i] ) ) ) { Q_strncpyz( name, "Player", sizeof( name ) ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); break; } } } maxchars = MAX_NAME_CHARS; if( ent->r.client->isTV ) maxchars = min( maxchars + 10, MAX_NAME_BYTES-1 ); // Limit the name to MAX_NAME_CHARS printable characters // (non-ascii utf-8 sequences are currently counted as 2 or more each, sorry) COM_SanitizeColorString( va( "%s", name ), name, sizeof( name ), maxchars, COLOR_WHITE ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); trynum = 1; do { for( i = 0; i < gs.maxclients; i++ ) { other = game.edicts + 1 + i; if( !other->r.inuse || !other->r.client || other == ent ) continue; // if nick is already in use, try with (number) appended if( !Q_stricmp( colorless, COM_RemoveColorTokens( other->r.client->netname ) ) ) { if( trynum != 1 ) // remove last try name[strlen( name ) - strlen( va( "%s(%i)", S_COLOR_WHITE, trynum-1 ) )] = 0; // make sure there is enough space for the postfix trylen = strlen( va( "%s(%i)", S_COLOR_WHITE, trynum ) ); if( (int)strlen( colorless ) + trylen > maxchars ) { COM_SanitizeColorString( va( "%s", name ), name, sizeof( name ), maxchars - trylen, COLOR_WHITE ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); } // add the postfix Q_strncatz( name, va( "%s(%i)", S_COLOR_WHITE, trynum ), sizeof( name ) ); Q_strncpyz( colorless, COM_RemoveColorTokens( name ), sizeof( colorless ) ); // go trough all clients again trynum++; break; } } } while( i != gs.maxclients && trynum <= MAX_CLIENTS ); Q_strncpyz( ent->r.client->netname, name, sizeof( ent->r.client->netname ) ); }
/* * G_SetClan */ static void G_SetClan( edict_t *ent, const char *original_clan ) { const char *invalid_values[] = { "console", "spec", "bot", "coach", "tv", NULL }; char clan[MAX_CLANNAME_BYTES]; char colorless[MAX_CLANNAME_BYTES]; int i; int c_ascii; int maxchars; if( !ent->r.client ) return; // we allow NULL to be passed for clan name if( ent->r.svflags & SVF_FAKECLIENT ) original_clan = "BOT"; else if( !original_clan ) original_clan = ""; Q_strncpyz( clan, original_clan, sizeof( clan ) ); COM_Compress( clan ); c_ascii = G_SanitizeUserString( clan, sizeof( clan ) ); if( !c_ascii ) clan[0] = colorless[0] = '\0'; else Q_strncpyz( colorless, COM_RemoveColorTokens( clan ), sizeof( colorless ) ); if( !( ent->r.svflags & SVF_FAKECLIENT ) ) { for( i = 0; invalid_values[i] != NULL; i++ ) { if( !Q_strnicmp( colorless, invalid_values[i], strlen( invalid_values[i] ) ) ) { clan[0] = colorless[0] = '\0'; break; } } } // clan names can not contain spaces Q_chrreplace( clan, ' ', '_' ); // clan names can not start with an ampersand { char *t; int len; t = clan; while( *t == '&' ) t++; len = strlen( clan ) - (t - clan); if( clan != t ) memmove( clan, t, len + 1 ); } maxchars = MAX_CLANNAME_CHARS; // Limit the name to MAX_NAME_CHARS printable characters // (non-ascii utf-8 sequences are currently counted as 2 or more each, sorry) COM_SanitizeColorString( va( "%s", clan ), clan, sizeof( clan ), maxchars, COLOR_WHITE ); Q_strncpyz( ent->r.client->clanname, clan, sizeof( ent->r.client->clanname ) ); }
/* * G_Match_Autorecord_Start */ void G_Match_Autorecord_Start( void ) { int team, i, playerCount; G_Match_SetAutorecordState( "start" ); // do not start autorecording if all playing clients are bots for( playerCount = 0, team = TEAM_PLAYERS; team < GS_MAX_TEAMS; team++ ) { // add our team info to the string for( i = 0; i < teamlist[team].numplayers; i++ ) { if( game.edicts[ teamlist[team].playerIndices[i] ].r.svflags & SVF_FAKECLIENT ) continue; playerCount++; break; // we only need one for this check } } if( playerCount && g_autorecord->integer ) { char datetime[17], players[MAX_STRING_CHARS]; time_t long_time; struct tm *newtime; // date & time time( &long_time ); newtime = localtime( &long_time ); Q_snprintfz( datetime, sizeof( datetime ), "%04d-%02d-%02d_%02d-%02d", newtime->tm_year + 1900, newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min ); // list of players Q_strncpyz( players, trap_GetConfigString( CS_MATCHNAME ), sizeof( players ) ); if( players[0] == '\0' ) { if( GS_InvidualGameType() ) { const char *netname; int team; edict_t *ent; for( team = TEAM_ALPHA; team < GS_MAX_TEAMS; team++ ) { if( !teamlist[team].numplayers ) continue; ent = game.edicts + teamlist[team].playerIndices[0]; netname = ent->r.client->netname; Q_strncatz( players, netname, sizeof( players ) ); if( team != GS_MAX_TEAMS - 1 ) Q_strncatz( players, " vs ", sizeof( players ) ); } } } if( players[0] != '\0' ) { char *t = strstr( players, " vs " ); if( t ) memcpy( t, "_vs_", strlen( "_vs_" ) ); Q_strncpyz( players, COM_RemoveJunkChars( COM_RemoveColorTokens( players ) ), sizeof( players ) ); } // combine Q_snprintfz( level.autorecord_name, sizeof( level.autorecord_name ), "%s_%s_%s%s%s_auto%04i", datetime, gs.gametypeName, level.mapname, players[0] == '\0' ? "" : "_", players, (int)brandom( 1, 9999 ) ); trap_Cmd_ExecuteText( EXEC_APPEND, va( "serverrecord %s\n", level.autorecord_name ) ); } }
void TVM_ChasePlayer( edict_t *ent, char *name, int followmode ) { int i; edict_t *e; gclient_t *client; int targetNum = -1; int oldTarget; bool can_follow = true; char colorlessname[MAX_NAME_BYTES]; client = ent->r.client; oldTarget = client->chase.target; if( oldTarget < 0 ) oldTarget = 0; if( !can_follow && followmode ) { TVM_PrintMsg( ent->relay, ent, "Chasecam follow mode unavailable\n" ); followmode = false; } if( ent->r.client->chase.followmode && !followmode ) TVM_PrintMsg( ent->relay, ent, "Disabling chasecam follow mode\n" ); // always disable chasing as a start memset( &client->chase, 0, sizeof( chasecam_t ) ); // locate the requested target if( name && name[0] ) { // find it by player names for( e = ent->relay->edicts + 1; PLAYERNUM( e ) < ent->relay->maxclients; e++ ) { if( !TVM_Chase_IsValidTarget( ent, e ) ) continue; Q_strncpyz( colorlessname, COM_RemoveColorTokens( e->r.client->pers.netname ), sizeof(colorlessname) ); if( !Q_stricmp( COM_RemoveColorTokens( name ), colorlessname ) ) { targetNum = PLAYERNUM( e ); break; } } // didn't find it by name, try by numbers if( targetNum == -1 ) { i = atoi( name ); if( i >= 0 && i < ent->relay->maxclients ) { e = ent->relay->edicts + 1 + i; if( TVM_Chase_IsValidTarget( ent, e ) ) targetNum = PLAYERNUM( e ); } } if( targetNum == -1 ) TVM_PrintMsg( ent->relay, ent, "Requested chasecam target is not available\n" ); } // try to reuse old target if we didn't find a valid one if( targetNum == -1 && oldTarget > 0 && oldTarget <= ent->relay->maxclients ) { e = ent->relay->edicts + 1 + oldTarget; if( TVM_Chase_IsValidTarget( ent, e ) ) targetNum = PLAYERNUM( e ); } // if we still don't have a target, just pick the first valid one if( targetNum == -1 ) { for( e = ent->relay->edicts + 1; PLAYERNUM( e ) < ent->relay->maxclients; e++ ) { if( !TVM_Chase_IsValidTarget( ent, e ) ) continue; targetNum = PLAYERNUM( e ); break; } } // make the client a ghost TVM_GhostClient( ent ); if( targetNum != -1 ) { // we found a target, set up the chasecam client->chase.target = targetNum + 1; client->chase.followmode = followmode; TVM_Chase_SetChaseActive( ent, true ); } else { // stay as observer ent->movetype = MOVETYPE_NOCLIP; TVM_SpectatorMode( ent ); TVM_CenterPrintMsg( ent->relay, ent, "No one to chase" ); } }