mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2024-11-23 12:32:09 +00:00
Merge pull request #73 from Pan7/sayto
sayto cmd with player name completion
This commit is contained in:
commit
fe619680f8
4 changed files with 381 additions and 0 deletions
|
@ -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 ();
|
||||||
|
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue