TIPS - Some helpful hints and examples for implementing cname 1.3. 
(by stratocaster)
------------------------------------------------------------------

  Silver once told me that I was one of the few people he knew of 
who had taken the time to install his version of cname, and 
implement it in more places than  what the diff file handles. After 
having installed cname 4 times, I think I  understand why. Since PG+ 
was written by several different people, all with different coding 
styles, it can be hard to understand where to put calls to get_cname(), 
and even harder to determine which variables go inside the parenthesis. 
Being a somewhat experienced coder myself, I hate to see good code go 
to waste, and I hate to see a good coder spend a lot of his own personal 
time coding something, only for it to not be used. So hopefully, by 
trying to explain things in more detail, and by giving some examples, 
I can help everyone who wants to implement cname to understand just how 
it works.
  Before we get started, please note that all the examples are taken 
directly from the code of my own talker. It should work on your talker, 
too... but if it doesn't, I'm not responsible in any way.

  Alrighty! If you haven't already done so, take a moment now to read the 
bottom of Silver's README file, paying special attention to the section 
entitled "How to finish the code:". This is VERY important. If you don't 
understand this, you'll have a rather hard time installing cname. Let's 
take a look.

  get_cname(p, q)

where: 	p = the pointer to the player whose name you wish to display
	q = the pointer to the player who you are showing the name to

Seems simple enough, doesn't it? The first thing that confused me about 
this was the p and q part. Very rarely in the code will you find an 
instance with p and q. You may have p, p2, scan, start, current_player, 
0, or any of a multitude of other variable names. This is a good example 
of what I said earlier, about PG+ being written by lots of different people 
with different styles. But don't fret... all you really need to remember 
is that the first variable is the person whose name you are displaying, 
and the second variable is the name of the person (or people) you're 
showing that name to.

So, let's look at a simple example. Open up admin2.c in your favorite text 
editor, and jump to the end of "void assist_player", where you'll see this:

  TELLPLAYER(p, " You assist %s.\n", p2->name);

  LOGF("assist", "%s assists %s", p->name, p2->name);

Here's a good example of where and where not to add cname. In the TELLPLAYER 
function, the first argument is always the pointer to the player that the 
message is being told to. The second argument is always the message being 
told. Any other arguments are variables that fit into the message. In this 
case, the message "You assist <name>." is being told to player p. <name> 
would be the name of the person being assisted. If we compare this to the 
original example, (the p's and q's) it's pretty simple to figure out. We 
should add a get_cname here, where p2 is the name being displayed, and p 
is the person that p2's coloured name is being displayed to. To put it 
simply, we'd rewrite it like this:

  TELLPLAYER(p, " You assist %s.\n", get_cname(p2, p));

Make sense? I hope so. ;) Now, onto that LOGF call. If you notice, it's 
got names in the message too. But in this case, it's probably better that 
we don't add cname. We know whose name is being displayed in this case... 
both p and p2... but we can't predict who is going to be seeing those 
names. So, whenever you see a ->name in a LOGF call, just skip it and 
continue merrily on your way.

Just because you can't immediately tell who the name is being displayed to 
doesn't always mean you shouldn't add cname. If you're not sure, take a 
moment to read through the entire function, and try to follow its logic. 
Often, you can find clues that will help you determine who will be seeing 
the name. For our first example of this, let's have a look at 
"void check_idle" in commands.c. This is a fairly large chunk of code, 
so I'm going to cut out a few sections, to make this example a little bit 
shorter.

void check_idle(player * p, char *str)
{
  player *scan, **list, **step;
  int i, n;
  char *oldstack, middle[80], namestring[40], *id;
  file *is_scan;
  int page, pages, count, not_idle = 0;

[snip]
One important thing to note here. Since we know that only players have 
coloured names, we can discount all the other variables when implementing 
the get_cname() function. In the above bit, you can see that we have 4 
different "players" here: p, scan, *list, and *step. Don't worry about the 
*'s too much for now, just note that they're there. 


  oldstack = stack;
  command_type |= SEE_ERROR;
  if (isalpha(*str) && !strstr(str, "everyone"))
  {
    align(stack);
    list = (player **) stack;
#ifdef ALLOW_MULTIS
    n = global_tag(p, str, 0);
#else
    n = global_tag(p, str);
#endif
    if (!n)
    {
      stack = oldstack;
      return;
    }
    id = stack;
    for (step = list, i = 0; i < n; i++, step++)
    {
      if (p->custom_flags & NOPREFIX)
      {
	strcpy(namestring, (*step)->name);
      }
      else if (*((*step)->pretitle))
      {
	sprintf(namestring, "%s %s", (*step)->pretitle, (*step)->name);
      }
      else
      {
	strcpy(namestring, (*step)->name);
      }
      if (!(*step)->idle)
      {
	sprintf(stack, "%s has just hit return.\n", namestring);
      }
      else
      {
	if ((*step)->idle_msg[0])
	{
	  sprintf(stack, "%s %s\n%s is %s idle\n", namestring,
		  (*step)->idle_msg, caps(gstring((*step))),
		  word_time((*step)->idle));
	}
	else
	{
	  sprintf(stack, "%s is %s idle.\n", namestring,
		  word_time((*step)->idle));
	}
      }
      stack = end_string(stack);
      tell_player(p, id);
      stack = id;
    }

Well, we finally got to the tell_player function, and we can see that in 
this instance, the message is being told to player p. But where's the 
message!?!? Let's backtrack. We can see here that the player is being told 
something named "id". What is "id", you ask? Well, according to the line 
below tell_player, "stack = id". Okay, seems simple enough so far. So... 
we need to look for where someone's name is printed to stack. Going back up 
a few lines, we see that a message is printed to the stack, and that the 
first variable is "namestring". That sounds pretty close, but it's not in 
->name format, and it's not one of the players that we identified at the 
beginning of the check_idle function. So, we go back up a little more, and 
we find 3 instances where (*step)->name is copied or printed to 
"namestring". It may look a little odd, but it fits what we're looking for. 
It's in ->name format, and *step is one of our players for this function. 
So, these three instances are where we need to add calls to get_cname(). 
And since we now know which name is being displayed, and who will be 
seeing it, this should be fairly simple. The first instance:

  strcpy(namestring, (*step)->name);

Should become:

  strcpy(namestring, get_cname(*step, p));

Okay, so that was pretty heavy. Feel free to take a break, re-read some 
stuff if you need to, because it's only going to get stranger from here. 
If you don't understand things up to this point, you're going to get even 
more lost from here on out... so go over those examples again, find a few 
functions of your own that are similar to these situations. The best way 
to learn is to practice!

So far, we've looked at players p, p2, and *step. I'd like to take a 
moment to go over another common player pointer that you'll see quite 
often. For this example, we're going to look at "list_whos_on_channel", 
in dsc.c:

void list_whos_on_channel(player * p, char *channel)
{
  player *scan;
  int counted = 0, i;
  char *oldstack = stack, temp[70];

  sprintf(temp, "People on the '%s' channel", channel);
  pstack_mid(temp);

  for (scan = flatlist_start; scan; scan = scan->flat_next)
    for (i = 0; i < MAX_CHANNELS; i++)
      if (!strcasecmp(scan->channels[i], channel))
      {
	counted++;
	if (scan->dsc_flags & BLOCK_ALL_CHANS)
	  stack += sprintf(stack, "(%s), ", scan->name);
	else
	  stack += sprintf(stack, "%s, ", scan->name);
      }

  if (counted)
  {
    stack = end_string(stack);
    oldstack[strlen(oldstack) - 2] = '.';
    oldstack[strlen(oldstack) - 1] = '\n';
    tell_player(p, oldstack);
    tell_player(p, LINE);
  }
  else
    tell_player(p, " No-one on the channel ?!? *le boggle*\n");
  stack = oldstack;
}

This time, we've got a new player on the scene, "scan". Just as in the 
last example, we've got a tell_player() function, where something called 
"oldstack" is being told to player p. We see that "stack = oldstack" in 
the last line, so we go back a few lines, where we see that scan->name is 
being printed to the stack. So we've got our three elements... where to 
put the get_cname call, the player being told to, and the player's name 
being displayed. In this case, there's more than one player's name being 
told to player p, but this function goes through all the players one at a 
time, so each time it gets a player, that name becomes scan->name. When 
the loop completes, and the function goes into the next loop, scan->name 
becomes the name of the next person on the list... and so on. So, while 
scan is actually a lot of different players, we can (in this instance) 
treat it as if it were just one name. Having said that, you should be able 
to connect the rest of the dots. This line:

  stack += sprintf(stack, " %s, ", scan->name);

becomes:

  stack += sprintf(stack, " %s, ", get_cname(scan, p));

So now we've covered pretty much all the different player types I 
mentioned in the beginning. There's only one left, and it can be the 
trickiest of the bunch. Deciding when to use it often takes a bit of 
looking around at the function you're working on, and thinking about how 
that function is used by the program, and ultimately, by the people logged
in. This mysterious player I'm talking about is "current_player". It can 
be pretty complex in some situations, but in the context we're using it 
in, it basically refers to any or all people viewing the message. Let's 
take a look at "void connect_channels" in dsc.c:

void connect_channels(player * p)
{
  char *oldstack = stack;
  int i;

  if (p->dsc_flags & BLOCK_ALL_CHANS)
    return;

  for (i = 0; i < MAX_CHANNELS; i++)
    if (*(p->channels[i]))
    {
      sprintf(stack, "[%s] ++ %s logs in and joins this channel ++\n", 
		p->channels[i], p->name);
      stack = end_string(stack);
      tell_channels(p->channels[i], oldstack);
      stack = oldstack;
    }
}

Going by the guidelines we've used up to this point, we see that we've got
player p, and that's the only player listed. Instead of tell_player, we've
got tell_channels, but it looks similar enough at first glance. A message 
is being put on the stack, and then passed via tell_channels, and we've 
got a p->name... but there's something missing! We've only got one player! 
Who are we telling this message to? Eek! Panic attack! Okay, maybe not. 
Take a look at the message that's being displayed:

  "[<some channel>] ++ <name> logs in and joins this channel ++

Think about that for a second. Who would be seeing this message? Anyone
who's currently on <some channel> when player p connects to the talker.
This could be one person, or nobody, or lots of people. So how do we
know? Well, that's where current_player comes in. Since current_player
refers to any or all people viewing a message, it seems that this would
be a good place to use it. Hence, this line:

  sprintf(stack, "[%s] ++ %s logs in and joins this channel ++\n", 
		p->channels[i], p->name);

becomes:

  sprintf(stack, "[%s] ++ %s logs in and joins this channel ++\n",
	  p->channels[i], get_cname(p, current_player));

So now, you may be thinking, "If current_player refers to the person
or people viewing the message, why not use it everywhere?" Well, it's
not that simple. With TELLPLAYER and tell_player, you are specifically
sending a message from the program to ONE person. With functions like
tell_channels, tell_room, and TELLROOM, you could only be telling one
person, but you could be sending the message to a lot of people. If you
were sending a message to one specific person, using current_player
would be a bad idea in general, and just wouldn't work in other places.

Let's look at another instance of where you would need to use 
current_player, and I'll demonstrate a point that Silver made in the
README file. Take a look at "void eat_item" in items.c. Again, I'm 
going to snip out some of the code here, 'cause I just noticed how
long this file is getting. ;)

void eat_item(player * p, char *str)
{

  item *i;
  struct s_item *s;

[snip]

  else
  {
    TELLROOM(p->location, "%s munches happily on a delicious looking %s.\n", 
	     p->name, s->name);
    TELLPLAYER(p, "You smile as you eat the delicious %s in one bite. Yum!\n", 
	       s->name);

[rest snipped]

Now, here we've got a TELLROOM, and a TELLPLAYER. Let's look at the 
room function first. We've got p->name and s->name. Oddly, s is in 
the ->name format, but it doesn't seem to be of type player. Which
means we only have one player: p. If we stop to think about it for
a moment, this is the message that's displayed to the entire room
when someone eats an edible item. So, to put some colour into it:

  TELLPLAYER(p->location, "%s munches happily on a delicious looking %s.\n", 
		get_cname(p, current_player), s->name);

But what about that crazy s!?! Doesn't it get colour too? It sure 
doesn't. Remember what Silver said in the README about not changing
every single instance of ->name? This is one of those. Like I said
earlier, it's NOT a player. Actually, s->name, in this case, is the
name of a saved item. Since only players have coloured names, we 
merely skip this and pretend it wasn't there. ;)

While we're talking about that part of README, I think it's 
worthwhile to note that all the instances of get_cname() so
far have been used in tell_player, TELLPLAYER, TELLROOM, and
similar function calls. Like Silver said, you should only
modify ->name bits if they are displayed to a player.

As this file has gotten pretty long, I'm going to touch on
one more point before I finish. One of the major problems I had
when installing cname was that it messed up preformatted lists.
What are preformatted lists? Take a look at 'lsu' on your talker
when there's more than one staff member on. See how the columns
line up so nicely? Well, simply adding cname in those lists 
causes everything to get knocked out of whack, and sometimes it
even cuts off parts of people's names. Let's have a look at a
chunk of code from "void lsu" in admin2.c:

  for (scan = flatlist_start; scan; scan = scan->flat_next)
  {
    prestack = stack;
    if (scan->location && scan->residency & PSU)
    {
      if (p->residency & PSU && *str != '-')
      {
	count++;
	*stack = ' ';
	stack++;
	sprintf(stack, " %-18s  ", scan->name);
	stack = strchr(stack, 0);
	if (scan->saved_residency & CODER)
	  sprintf(stack, "%-18.18s  ", get_config_msg("coder_name"));
	else if (scan->saved_residency & HCADMIN)

If you look at the entire function, you'll see that the call here
should be "get_cname(scan, p)". But, because the spacing is 
calculated before the colour codes are converted, it makes quite
a mess of things. So, after some brainstorming, I came up with the
following:

  sprintf(stack, " %-18s  ", scan->name);

becomes:

  sprintf(stack, " %-*s  ", 
	((MAX_NAME - strlen(scan->name)) + strlen(get_cname(scan, p))), 
	 get_cname(scan, p));

I don't know if that's the best way to do it or not, but it works
fairly well; I've put that in just about every bit of code on my
talker that uses cname with a "%-18s" (or some number other than
18), and it's worked every time. You're welcome to use that, but
like I said at the beginning, don't blame me if it doesn't work.

Well, I guess that's about it for now. I hope that helps. If 
there are any questions, feel free to email me at
stratocaster@notnet.co.uk, but please try to keep your questions
specific. Good luck!

-- Will Fischer 11/02/01