mirror of
https://github.com/ioquake/ioq3.git
synced 2024-11-10 07:11:46 +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
|
||||
|
@ -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
|
||||
|
@ -3598,6 +3692,10 @@ void CL_Init( void ) {
|
|||
Cmd_AddCommand ("model", CL_SetModel_f );
|
||||
Cmd_AddCommand ("video", CL_Video_f );
|
||||
Cmd_AddCommand ("stopvideo", CL_StopVideo_f );
|
||||
if( !com_dedicated->integer ) {
|
||||
Cmd_AddCommand ("sayto", CL_Sayto_f );
|
||||
Cmd_SetCommandCompletionFunc( "sayto", CL_CompletePlayerName );
|
||||
}
|
||||
CL_InitRef();
|
||||
|
||||
SCR_Init ();
|
||||
|
|
|
@ -3585,3 +3585,177 @@ qboolean Com_IsVoipTarget(uint8_t *voipTargets, int voipTargetsSize, int clientN
|
|||
|
||||
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 );
|
||||
void Field_CompleteCommand( char *cmd,
|
||||
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
|
||||
// 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_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
|
||||
|
@ -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
|
||||
|
@ -1453,6 +1555,8 @@ void SV_AddOperatorCommands( void ) {
|
|||
if( com_dedicated->integer ) {
|
||||
Cmd_AddCommand ("say", SV_ConSay_f);
|
||||
Cmd_AddCommand ("tell", SV_ConTell_f);
|
||||
Cmd_AddCommand ("sayto", SV_ConSayto_f);
|
||||
Cmd_SetCommandCompletionFunc( "sayto", SV_CompletePlayerName );
|
||||
}
|
||||
|
||||
Cmd_AddCommand("rehashbans", SV_RehashBans_f);
|
||||
|
|
Loading…
Reference in a new issue