Private messaging


(*Shadow* Commando) #1

Hi,

Does anyone know how to add private messaging like in shrubet.
/m [player] [message]

Hope someone can help me…

Thanks


(Demolama) #2

first thing is you need to add the command to the bottom of cmd_reference_t aCommandInfo[]

	{ "m",		qtrue,	qtrue,	G_say_friend_cmd, "<msg>:^7 Sends a private message to client" },

G_say_friend_cmd is the function you need to find the player you are looking for… my suggestion is to look at the function ClientNumberFromString for ideas and then eventually call the function Cmd_SayPlayer

Cmd_SayPlayer_f(ent, player, SAY_FRIEND, qfalse);

void Cmd_SayPlayer_f( gentity_t *ent, gentity_t *player, int mode, qboolean arg0 )
{
	if(trap_Argc() < 2 && !arg0) return;
	G_Say(ent, player, mode, ConcatArgs(((arg0) ? 1 : 2)));
	

}

this will parse the argument so that only the 3rd argument is sent to be displayed
G_say add this


 case SAY_FRIEND:
		 
        G_LogPrintf( "Priv To: %s: %s
", target->client->pers.netname, chatText );
		Com_sprintf (name, sizeof(name), "^1Priv To:^7(%s): ", target->client->pers.netname);
		color = COLOR_RED;
		Q_strncpyz( text, chatText, sizeof(text) );
		G_SayTo( target,ent, mode, color, name, text, localize );
		 
		 
		G_LogPrintf( "Priv From: %s: %s
", ent->client->pers.netname, chatText );
		Com_sprintf (name, sizeof(name), "^1Priv From:^7(%s): ", ent->client->pers.netname);
		color = COLOR_RED;
		Q_strncpyz( text, chatText, sizeof(text) );
		G_SayTo(ent, target, mode, color, name, text, localize );
	
		break;

any if statement after the switch add a check mode != SAY_FRIEND so that we dont display the private message more than once

in bg_public.h dont forget to #define SAY_FRIEND

#define SAY_ALL		0
#define SAY_TEAM	1
#define	SAY_BUDDY	2
#define SAY_TEAMNL	3
#define SAY_FRIEND  4

the problem I have right now with the current code above in the G_say_friend_cmd function is where I need to compare the string arg and the clients name to determine who to send the message to… I can only get the private message to work with /player numbers or the first few letters of the name at the present time by using a modified ClientNumberFromString function.

the last couple of tries at writing a search function werent all that good :confused: and I really dont have the time with finals coming up to make one

if anyone has the time to write a search function that will compare the string argument with a client’s name everyone will be much happier because what I posted above will work with the right string comparision


(bacon) #3

Did you try stripping the colours from the names then using strstr()?


(Demolama) #4

wow I didnt even think about strstr lol doh

and look at that… thats exactly what I needed

:clap:

thanks for reminding me about that function


(*Shadow* Commando) #5

How do i make it work with only a few letters of someones name, as example commando
I made the following code now, but that only works with numbers. I tried it with the strstr()
what bacon said above, but i can’t get it working.

void G_say_friend_cmd (gentity_t *ent) {
	int		pid;
	char	arg[MAX_TOKEN_CHARS];
	gentity_t *player;

	trap_Argv(1, arg, sizeof(arg));
	if((pid = ClientNumberFromString(ent, arg)) == -1) return;
	player = g_entities + pid;

	if(player->client->sess.sessionTeam != TEAM_SPECTATOR) {
		Cmd_SayPlayer_f(ent, player, SAY_FRIEND, qfalse);
	}
}

I hope someone can help me.


(SCDS_reyalP) #6

Note a disadvantage to matching substrings (as etpro does) is that if your buddies name is Joe, and there is also a JoeBob on the server, you can’t just send your message to Joe without also sending to JoeBob. Or, if you only send to one, then there is no way to be sure it goes to the right person. So allowing player numbers might be a good idea (but then what about people named ‘1’ :bash:), or some kind of wild card :banana:

Extra credit for not letting people private message everyone on the server at the same time. :moo:


(*Shadow* Commando) #7

You’ve got a point there, maybe it is more usefull with numbers then with names. Shrubet has te same problem like ETpro.
Guess i’ll stick to the players numbers then.
Thanks for the reply


(tjw) #8

Here is a function I wrote for doing partial name matches.


/*
==================
ClientNumbersFromString

Sets plist to an array of integers that represent client numbers that have
names that are a partial match for s. List is terminated by a -1.

Returns number of matching clientids.
==================
*/
int ClientNumbersFromString( char *s, int *plist) {
        gclient_t *p;
        int i, found = 0;
        char s2[MAX_STRING_CHARS];
        char n2[MAX_STRING_CHARS];
        char *m;
        qboolean is_slot = qtrue;

        //plist = G_Alloc(sizeof(int)*(MAX_CLIENTS+1));
        *plist = -1;

        // if a number is provided that is not a name, it might be slot #
        for(i=0; i<strlen(s); i++) {
                if(s[i] < '0' || s[i] > '9') {
                        is_slot = qfalse;
                        break;
                }
        }
        if(is_slot) {
                i = atoi(s);
                if(i >= 0 && i < level.maxclients) {
                        p = &level.clients[i];
                        if(p->pers.connected == CON_CONNECTED) {
                                *plist++ = i;
                                *plist = -1;
                                return 1;
                        }
                }
        }

        // now look for name matches
        SanitizeString(s, s2, qtrue);
        if(strlen(s2) < 1) return 0;
        for(i=0; i < level.maxclients; i++) {
                p = &level.clients[i];
                if(p->pers.connected != CON_CONNECTED) continue;
                SanitizeString(p->pers.netname, n2, qtrue);
                m = strstr(n2, s2);
                if(m != NULL) {
                        *plist++ = i;
                        found++;
                }
        }
        *plist = -1;
        return found;
}

it doesn’t check for thie size of the plist paramter sure you pass it a big enought integer array:


int pids[MAX_CLIENTS];


(Chruker) #9

that if your buddies name is Joe, and there is also a JoeBob on the server, you can’t just send your message to Joe without also sending to JoeBob.

However the wildcard usage is often used for private group talk. Ex. chatting with your clan buddies.


(tjw) #10

You should be able to use slot number in this senario, e.g. ‘/m 7 hi joe’ ( if 7 is Joe’s slot #)

The function I posted above accounts for this.


(*Shadow* Commando) #11

Only where do i need to put the function?


(tjw) #12

Probably in g_cmds.c next to ClientNumberFromString().

Here’s my G_PrivateMessage() function:


void G_PrivateMessage(gentity_t *ent) 
{
	int pids[MAX_CLIENTS];
	char name[MAX_NAME_LENGTH];
	char *msg;
	int pcount;
	int i;
	gentity_t *tmpent;
	qboolean sent = qfalse;

	if(!g_privateMessages.integer) return;
	if(trap_Argc() < 3) {
		CP("print \"usage: /m [name|slot#] [message]
\"");
		return;
	}
	trap_Argv(1, name, sizeof(name));
	msg = ConcatArgs(2);
	pcount = ClientNumbersFromString(name, pids);
	for(i=0; i<pcount; i++) {
		if(pids[i] == ent-g_entities) continue;
		sent = qtrue;
		tmpent = &g_entities[pids[i]];
		CPx(pids[i], va(
			"chat \"%s^7 -> %s^7: (%d recipients^7): ^3%s^7\"",
			ent->client->pers.netname,
			tmpent->client->pers.netname,
			pcount,
			msg));
		CPx(pids[i], va("cp \"^3private message from ^7%s^7\"",
			ent->client->pers.netname));
		CP(va("print \"private message sent to %s: ^3%s^7
\"",
			tmpent->client->pers.netname,
			msg
			));
	}
	if(!sent) {
		CP("print \"player not found
\"");
	}
}

This is called at the end if else block in ClientCommand() at the end of g_cmds.c


	else if (!Q_stricmp (cmd, "m")) {
		G_PrivateMessage(ent);
	}
	else {
		trap_SendServerCommand( clientNum, va("print \"unknown cmd[lof] %s
\"", cmd ) );
	}