Merge pull request #73 from Pan7/sayto

sayto cmd with player name completion
This commit is contained in:
Zachary J. Slater 2015-12-28 09:04:19 -08:00
commit fe619680f8
4 changed files with 381 additions and 0 deletions

View file

@ -1808,6 +1808,50 @@ static void CL_CompleteRcon( char *args, int argNum )
} }
} }
/*
==================
CL_CompletePlayerName
==================
*/
static void CL_CompletePlayerName( char *args, int argNum )
{
if( argNum == 2 )
{
char names[MAX_CLIENTS][MAX_NAME_LENGTH];
char *namesPtr[MAX_CLIENTS];
int i;
int clientCount;
int nameCount;
const char *info;
const char *name;
//configstring
info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_SERVERINFO];
clientCount = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
nameCount = 0;
for( i = 0; i < clientCount; i++ ) {
if( i == clc.clientNum )
continue;
info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_PLAYERS+i];
name = Info_ValueForKey( info, "n" );
if( name[0] == '\0' )
continue;
Q_strncpyz( names[nameCount], name, sizeof(names[nameCount]) );
Q_CleanStr( names[nameCount] );
namesPtr[nameCount] = names[nameCount];
nameCount++;
}
qsort( (void*)namesPtr, nameCount, sizeof( namesPtr[0] ), Com_strCompare );
Field_CompletePlayerName( namesPtr, nameCount );
}
}
/* /*
===================== =====================
CL_Rcon_f CL_Rcon_f
@ -3396,6 +3440,56 @@ static void CL_GenerateQKey(void)
} }
} }
void CL_Sayto_f( void ) {
char *rawname;
char name[MAX_NAME_LENGTH];
char cleanName[MAX_NAME_LENGTH];
const char *info;
int count;
int i;
int clientNum;
char *p;
if ( Cmd_Argc() < 3 ) {
Com_Printf ("sayto <player name> <text>\n");
return;
}
rawname = Cmd_Argv(1);
Com_FieldStringToPlayerName( name, MAX_NAME_LENGTH, rawname );
info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_SERVERINFO];
count = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
clientNum = -1;
for( i = 0; i < count; i++ ) {
info = cl.gameState.stringData + cl.gameState.stringOffsets[CS_PLAYERS+i];
Q_strncpyz( cleanName, Info_ValueForKey( info, "n" ), sizeof(cleanName) );
Q_CleanStr( cleanName );
if ( !Q_stricmp( cleanName, name ) ) {
clientNum = i;
break;
}
}
if( clientNum <= -1 )
{
Com_Printf ("No such player name: %s.\n", name);
return;
}
p = Cmd_ArgsFrom(2);
if ( *p == '"' ) {
p++;
p[strlen(p)-1] = 0;
}
CL_AddReliableCommand(va("tell %i \"%s\"", clientNum, p ), qfalse);
}
/* /*
==================== ====================
CL_Init CL_Init
@ -3598,6 +3692,10 @@ void CL_Init( void ) {
Cmd_AddCommand ("model", CL_SetModel_f ); Cmd_AddCommand ("model", CL_SetModel_f );
Cmd_AddCommand ("video", CL_Video_f ); Cmd_AddCommand ("video", CL_Video_f );
Cmd_AddCommand ("stopvideo", CL_StopVideo_f ); Cmd_AddCommand ("stopvideo", CL_StopVideo_f );
if( !com_dedicated->integer ) {
Cmd_AddCommand ("sayto", CL_Sayto_f );
Cmd_SetCommandCompletionFunc( "sayto", CL_CompletePlayerName );
}
CL_InitRef(); CL_InitRef();
SCR_Init (); SCR_Init ();

View file

@ -3585,3 +3585,177 @@ qboolean Com_IsVoipTarget(uint8_t *voipTargets, int voipTargetsSize, int clientN
return qfalse; return qfalse;
} }
/*
===============
Field_CompletePlayerName
===============
*/
static qboolean Field_CompletePlayerNameFinal( qboolean whitespace )
{
int completionOffset;
if( matchCount == 0 )
return qtrue;
completionOffset = strlen( completionField->buffer ) - strlen( completionString );
Q_strncpyz( &completionField->buffer[ completionOffset ], shortestMatch,
sizeof( completionField->buffer ) - completionOffset );
completionField->cursor = strlen( completionField->buffer );
if( matchCount == 1 && whitespace )
{
Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
completionField->cursor++;
return qtrue;
}
return qfalse;
}
static void Name_PlayerNameCompletion( const char **names, int nameCount, void(*callback)(const char *s) )
{
int i;
for( i = 0; i < nameCount; i++ ) {
callback( names[ i ] );
}
}
qboolean Com_FieldStringToPlayerName( char *name, int length, const char *rawname )
{
char hex[5];
int i;
int ch;
if( name == NULL || rawname == NULL )
return qfalse;
if( length <= 0 )
return qtrue;
for( i = 0; *rawname && i + 1 <= length; rawname++, i++ ) {
if( *rawname == '\\' ) {
Q_strncpyz( hex, rawname + 1, sizeof(hex) );
ch = Com_HexStrToInt( hex );
if( ch > -1 ) {
name[i] = ch;
rawname += 4; //hex string length, 0xXX
} else {
name[i] = *rawname;
}
} else {
name[i] = *rawname;
}
}
name[i] = '\0';
return qtrue;
}
qboolean Com_PlayerNameToFieldString( char *str, int length, const char *name )
{
const char *p;
int i;
int x1, x2;
if( str == NULL || name == NULL )
return qfalse;
if( length <= 0 )
return qtrue;
*str = '\0';
p = name;
for( i = 0; *p != '\0'; i++, p++ )
{
if( i + 1 >= length )
break;
if( *p <= ' ' )
{
if( i + 5 + 1 >= length )
break;
x1 = *p >> 4;
x2 = *p & 15;
str[i+0] = '\\';
str[i+1] = '0';
str[i+2] = 'x';
str[i+3] = x1 > 9 ? x1 - 10 + 'a' : x1 + '0';
str[i+4] = x2 > 9 ? x2 - 10 + 'a' : x2 + '0';
i += 4;
} else {
str[i] = *p;
}
}
str[i] = '\0';
return qtrue;
}
void Field_CompletePlayerName( char **names, int nameCount )
{
qboolean whitespace;
matchCount = 0;
shortestMatch[ 0 ] = 0;
if( nameCount <= 0 )
return;
Name_PlayerNameCompletion( names, nameCount, FindMatches );
if( completionString[0] == '\0' )
{
Com_PlayerNameToFieldString( shortestMatch, sizeof( shortestMatch ), names[ 0 ] );
}
//allow to tab player names
//if full player name switch to next player name
if( completionString[0] != '\0'
&& Q_stricmp( shortestMatch, completionString ) == 0
&& nameCount > 1 )
{
int i;
for( i = 0; i < nameCount; i++ ) {
if( Q_stricmp( names[ i ], completionString ) == 0 )
{
i++;
if( i >= nameCount )
{
i = 0;
}
Com_PlayerNameToFieldString( shortestMatch, sizeof( shortestMatch ), names[ i ] );
break;
}
}
}
if( matchCount > 1 )
{
Com_Printf( "]%s\n", completionField->buffer );
Name_PlayerNameCompletion( names, nameCount, PrintMatches );
}
whitespace = nameCount == 1? qtrue: qfalse;
if( !Field_CompletePlayerNameFinal( whitespace ) )
{
}
}
int QDECL Com_strCompare( const void *a, const void *b )
{
const char **pa = (const char **)a;
const char **pb = (const char **)b;
return strcmp( *pa, *pb );
}

View file

@ -760,6 +760,7 @@ void Field_CompleteFilename( const char *dir,
const char *ext, qboolean stripExt, qboolean allowNonPureFilesOnDisk ); const char *ext, qboolean stripExt, qboolean allowNonPureFilesOnDisk );
void Field_CompleteCommand( char *cmd, void Field_CompleteCommand( char *cmd,
qboolean doCommands, qboolean doCvars ); qboolean doCommands, qboolean doCvars );
void Field_CompletePlayerName( char **names, int count );
/* /*
============================================================== ==============================================================
@ -839,6 +840,10 @@ void Com_StartupVariable( const char *match );
// if match is NULL, all set commands will be executed, otherwise // if match is NULL, all set commands will be executed, otherwise
// only a set with the exact name. Only used during startup. // only a set with the exact name. Only used during startup.
qboolean Com_PlayerNameToFieldString( char *str, int length, const char *name );
qboolean Com_FieldStringToPlayerName( char *name, int length, const char *rawname );
int QDECL Com_strCompare( const void *a, const void *b );
extern cvar_t *com_developer; extern cvar_t *com_developer;
extern cvar_t *com_dedicated; extern cvar_t *com_dedicated;

View file

@ -1303,6 +1303,71 @@ static void SV_ConTell_f(void) {
} }
/*
==================
SV_ConSayto_f
==================
*/
static void SV_ConSayto_f(void) {
char *p;
char text[1024];
client_t *cl;
char *rawname;
char name[MAX_NAME_LENGTH];
char cleanName[MAX_NAME_LENGTH];
client_t *saytocl;
int i;
// make sure server is running
if ( !com_sv_running->integer ) {
Com_Printf( "Server is not running.\n" );
return;
}
if ( Cmd_Argc() < 3 ) {
Com_Printf ("Usage: sayto <player name> <text>\n");
return;
}
rawname = Cmd_Argv(1);
//allowing special characters in the console
//with hex strings for player names
Com_FieldStringToPlayerName( name, MAX_NAME_LENGTH, rawname );
saytocl = NULL;
for ( i=0, cl=svs.clients ; i < sv_maxclients->integer ; i++,cl++ ) {
if ( !cl->state ) {
continue;
}
Q_strncpyz( cleanName, cl->name, sizeof(cleanName) );
Q_CleanStr( cleanName );
if ( !Q_stricmp( cleanName, name ) ) {
saytocl = cl;
break;
}
}
if( !saytocl )
{
Com_Printf ("No such player name: %s.\n", name);
return;
}
strcpy (text, "console_sayto: ");
p = Cmd_ArgsFrom(2);
if ( *p == '"' ) {
p++;
p[strlen(p)-1] = 0;
}
strcat(text, p);
SV_SendServerCommand(saytocl, "chat \"%s\"", text);
}
/* /*
================== ==================
SV_Heartbeat_f SV_Heartbeat_f
@ -1407,6 +1472,43 @@ static void SV_CompleteMapName( char *args, int argNum ) {
} }
} }
/*
==================
SV_CompletePlayerName
==================
*/
static void SV_CompletePlayerName( char *args, int argNum ) {
if( argNum == 2 ) {
char names[MAX_CLIENTS][MAX_NAME_LENGTH];
char *namesPtr[MAX_CLIENTS];
client_t *cl;
int i;
int nameCount;
int clientCount;
nameCount = 0;
clientCount = sv_maxclients->integer;
for ( i=0, cl=svs.clients ; i < clientCount; i++,cl++ ) {
if ( !cl->state ) {
continue;
}
if( i >= MAX_CLIENTS ) {
break;
}
Q_strncpyz( names[nameCount], cl->name, sizeof(names[nameCount]) );
Q_CleanStr( names[nameCount] );
namesPtr[nameCount] = names[nameCount];
nameCount++;
}
qsort( (void*)namesPtr, nameCount, sizeof( namesPtr[0] ), Com_strCompare );
Field_CompletePlayerName( namesPtr, nameCount );
}
}
/* /*
================== ==================
SV_AddOperatorCommands SV_AddOperatorCommands
@ -1453,6 +1555,8 @@ void SV_AddOperatorCommands( void ) {
if( com_dedicated->integer ) { if( com_dedicated->integer ) {
Cmd_AddCommand ("say", SV_ConSay_f); Cmd_AddCommand ("say", SV_ConSay_f);
Cmd_AddCommand ("tell", SV_ConTell_f); Cmd_AddCommand ("tell", SV_ConTell_f);
Cmd_AddCommand ("sayto", SV_ConSayto_f);
Cmd_SetCommandCompletionFunc( "sayto", SV_CompletePlayerName );
} }
Cmd_AddCommand("rehashbans", SV_RehashBans_f); Cmd_AddCommand("rehashbans", SV_RehashBans_f);