/* ************************************************************************
* file: comm.c , Communication module. Part of DIKUMUD *
* Usage: Communication, central game loop. *
* Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
* All Rights Reserved *
* Using *any* part of DikuMud without having read license.doc is *
* violating our copyright.
************************************************************************* */
#include "os.h"
#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "prototypes.h"
#define DFLT_PORT 4000 /* default port */
#define MAX_NAME_LENGTH 15
#define MAX_HOSTNAME 256
#define OPT_USEC 250000 /* time delay corresponding to 4 passes/sec */
/* externs */
/* extern struct char_data *character_list; */
extern struct room_data *world; /* In db.c */
extern int top_of_world; /* In db.c */
extern struct time_info_data time_info; /* In db.c */
extern char help[];
extern bool wizlock;
/* local globals */
struct descriptor_data *descriptor_list, *next_to_process;
int lawful = 0; /* work like the game regulator */
int slow_death = 0; /* Shut her down, Martha, she's sucking mud */
int shutdown_server = 0; /* clean shutdown */
#if defined __FreeBSD__
int greboot = 0; /* reboot the game after a shutdown */
#else
int reboot = 0; /* reboot the game after a shutdown */
#endif
int no_specials = 0; /* Suppress ass. of special routines */
int maxdesc, avail_descs;
int tics = 0; /* for extern checkpointing */
int get_from_q (struct txt_q *queue, char *dest);
/* write_to_q is in comm.h for the macro */
void run_the_game (int port);
void game_loop (SOCKET s);
int init_socket (int port);
int new_connection (SOCKET s);
int new_descriptor (SOCKET s);
int process_output (struct descriptor_data *t);
int process_input (struct descriptor_data *t);
void close_sockets (int s);
void close_socket (struct descriptor_data *d);
struct timeval timediff (struct timeval *a, struct timeval *b);
void flush_queues (struct descriptor_data *d);
void nonblock (SOCKET s);
void parse_name (struct descriptor_data *desc, char *arg);
int load (void);
void coma (SOCKET s);
/* extern fcnts */
struct char_data *make_char (char *name, struct descriptor_data *desc);
void boot_db (void);
void zone_update (void);
void affect_update (void); /* In spells.c */
void point_update (void); /* In limits.c */
void free_char (struct char_data *ch);
void mobile_activity (void);
void string_add (struct descriptor_data *d, char *str);
void perform_violence (void);
void stop_fighting (struct char_data *ch);
void show_string (struct descriptor_data *d, char *input);
void gr (SOCKET s);
void check_reboot (void);
/* *********************************************************************
* main game loop and related stuff *
********************************************************************* */
int main (int argc, char **argv)
{
int port;
char buf[512];
int pos = 1;
char *dir;
port = DFLT_PORT;
dir = DFLT_DIR;
while ((pos < argc) && (*(argv[pos]) == '-')) {
switch (*(argv[pos] + 1)) {
case 'l':
lawful = 1;
log ("Lawful mode selected.");
break;
case 'd':
if (*(argv[pos] + 2))
dir = argv[pos] + 2;
else if (++pos < argc)
dir = argv[pos];
else {
log ("Directory arg expected after option -d.");
exit (0);
}
break;
case 's':
no_specials = 1;
log ("Suppressing assignment of special routines.");
break;
default:
sprintf (buf, "Unknown option -% in argument string.",
*(argv[pos] + 1));
log (buf);
break;
}
pos++;
}
if (pos < argc)
if (!isdigit ((int)*argv[pos])) {
fprintf (stderr, "Usage: %s [-l] [-s] [-d pathname] [ port # ]\n",
argv[0]);
exit (0);
} else if ((port = atoi (argv[pos])) <= 1024) {
printf ("Illegal port #\n");
exit (0);
}
sprintf (buf, "Running game on port %d.", port);
log (buf);
#ifdef _MSC_VER
if (_chdir (dir) < 0) {
#else
if (chdir (dir) < 0) {
#endif
perror ("chdir");
exit (0);
}
sprintf (buf, "Using %s as data directory.", dir);
log (buf);
OS_SRAND (time (0));
WIN32STARTUP
run_the_game (port);
WIN32CLEANUP
return (0);
}
#define PROFILE(x)
/* Init sockets, run game, and cleanup sockets */
void run_the_game (int port)
{
int s;
PROFILE (extern etext ();
)
PROFILE (monstartup ((int) 2, etext);
)
descriptor_list = NULL;
log ("Signal trapping.");
signal_setup ();
log ("Opening mother connection.");
s = init_socket (port);
if (lawful && load () >= 6) {
log ("System load too high at startup.");
coma (s);
}
boot_db ();
log ("Entering game loop.");
game_loop (s);
close_sockets (s);
PROFILE (monitor (0);
)
#if defined __FreeBSD__
if (greboot) {
#else
if (reboot) {
#endif
log ("Rebooting.");
WIN32CLEANUP
exit (52); /* what's so great about HHGTTG, anyhow? */
}
log ("Normal termination of game.");
}
/* Accept new connects, relay commands, and call 'heartbeat-functs' */
void game_loop (SOCKET s)
{
int tmp_room, old_len;
fd_set input_set, output_set, exc_set, dummy_set;
struct timeval last_time, now, timespent, timeout, null_time;
static struct timeval opt_time;
char comm[MAX_INPUT_LENGTH];
struct descriptor_data *t, *point, *next_point;
int pulse = 0;
null_time.tv_sec = 0;
null_time.tv_usec = 0;
opt_time.tv_usec = OPT_USEC; /* Init time values */
opt_time.tv_sec = 0;
gettimeofday (&last_time, NULL);
#ifdef WIN32
maxdesc = 1;
#else
maxdesc = s;
#endif
avail_descs = getdtablesize () - 2; /* !! Change if more needed !! */
/* Main loop */
while (!shutdown_server) {
/* Check what's happening out there */
FD_ZERO (&input_set);
FD_ZERO (&output_set);
FD_ZERO (&exc_set);
FD_SET (s, &input_set);
#ifdef WIN32
FD_ZERO (&dummy_set);
FD_SET (s, &dummy_set);
#endif
for (point = descriptor_list; point; point = point->next) {
FD_SET (point->descriptor, &input_set);
FD_SET (point->descriptor, &exc_set);
FD_SET (point->descriptor, &output_set);
}
/* check out the time */
gettimeofday (&now, NULL);
timespent = timediff (&now, &last_time);
timeout = timediff (&opt_time, ×pent);
last_time.tv_sec = now.tv_sec + timeout.tv_sec;
last_time.tv_usec = now.tv_usec + timeout.tv_usec;
if (last_time.tv_usec >= 1000000) {
last_time.tv_usec -= 1000000;
last_time.tv_sec++;
}
block_signals();
if (select (maxdesc + 1, &input_set, &output_set, &exc_set, &null_time)
< 0) {
perror ("Select poll");
WIN32CLEANUP
exit (1);
}
#ifdef WIN32 /* windows select demands a valid fd_set */
if (select (0, (fd_set *) 0, (fd_set *) 0, &dummy_set, &timeout) == SOCKET_ERROR) {
#else
if (select (0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) == SOCKET_ERROR) {
#endif
perror ("Select sleep");
WIN32CLEANUP
exit (1);
}
restore_signals();
/* Respond to whatever might be happening */
/* New connection? */
if (FD_ISSET (s, &input_set))
if (new_descriptor (s) < 0)
perror ("New connection");
/* kick out the freaky folks */
for (point = descriptor_list; point; point = next_point) {
next_point = point->next;
if (FD_ISSET (point->descriptor, &exc_set)) {
FD_CLR (point->descriptor, &input_set);
FD_CLR (point->descriptor, &output_set);
close_socket (point);
}
}
for (point = descriptor_list; point; point = next_point) {
next_point = point->next;
if (FD_ISSET (point->descriptor, &input_set))
if (process_input (point) < 0)
close_socket (point);
}
/* process_commands; */
for (point = descriptor_list; point; point = next_to_process) {
next_to_process = point->next;
if ((--(point->wait) <= 0) && get_from_q (&point->input, comm)) {
if (point->character && point->connected == CON_PLYNG &&
point->character->specials.was_in_room != NOWHERE) {
if (point->character->in_room != NOWHERE)
char_from_room (point->character);
char_to_room (point->character,
point->character->specials.was_in_room);
point->character->specials.was_in_room = NOWHERE;
act ("$n has returned.", TRUE, point->character, 0, 0, TO_ROOM);
affect_total (point->character);
}
point->wait = 1;
if (point->character)
point->character->specials.timer = 0;
point->prompt_mode = 1;
if (point->str)
string_add (point, comm);
else if (!point->connected)
if (point->showstr_point)
show_string (point, comm);
else
command_interpreter (point->character, comm);
else
nanny (point, comm);
}
}
for (point = descriptor_list; point; point = next_point) {
next_point = point->next;
if (FD_ISSET (point->descriptor, &output_set) && point->output.head)
if (process_output (point) < 0)
close_socket (point);
else
point->prompt_mode = 1;
}
/* give the people some prompts */
for (point = descriptor_list; point; point = point->next)
if (point->prompt_mode) {
if (point->str)
write_to_descriptor (point->descriptor, "] ");
else if (!point->connected)
if (point->showstr_point)
write_to_descriptor (point->descriptor, "*** Press return ***");
else
write_to_descriptor (point->descriptor, "> ");
point->prompt_mode = 0;
}
/* handle heartbeat stuff */
/* Note: pulse now changes every 1/4 sec */
pulse++;
if (!(pulse % PULSE_ZONE)) {
zone_update ();
if (lawful)
gr (s);
}
if (!(pulse % PULSE_MOBILE))
mobile_activity ();
if (!(pulse % PULSE_VIOLENCE))
perform_violence ();
if (!(pulse % (SECS_PER_MUD_HOUR * 4))) {
weather_and_time (1);
affect_update ();
point_update ();
if (time_info.hours == 1)
update_time ();
}
if (pulse >= 2400) {
pulse = 0;
if (lawful)
night_watchman ();
check_reboot ();
}
tics++; /* tics since last checkpoint signal */
}
}
/* ******************************************************************
* general utility stuff (for local use) *
****************************************************************** */
int get_from_q (struct txt_q *queue, char *dest)
{
struct txt_block *tmp;
/* Q empty? */
if (!queue->head)
return (0);
tmp = queue->head;
strcpy (dest, queue->head->text);
queue->head = queue->head->next;
free (tmp->text);
free (tmp);
return (1);
}
void write_to_q (char *txt, struct txt_q *queue)
{
struct txt_block *new;
CREATE (new, struct txt_block, 1);
CREATE (new->text, char, strlen (txt) + 1);
strcpy (new->text, txt);
/* Q empty? */
if (!queue->head) {
new->next = NULL;
queue->head = queue->tail = new;
} else {
queue->tail->next = new;
queue->tail = new;
new->next = NULL;
}
}
struct timeval timediff (struct timeval *a, struct timeval *b)
{
struct timeval rslt, tmp;
tmp = *a;
if ((rslt.tv_usec = tmp.tv_usec - b->tv_usec) < 0) {
rslt.tv_usec += 1000000;
--(tmp.tv_sec);
}
if ((rslt.tv_sec = tmp.tv_sec - b->tv_sec) < 0) {
rslt.tv_usec = 0;
rslt.tv_sec = 0;
}
return (rslt);
}
/* Empty the queues before closing connection */
void flush_queues (struct descriptor_data *d)
{
char dummy[MAX_STRING_LENGTH];
while (get_from_q (&d->output, dummy));
while (get_from_q (&d->input, dummy));
}
/* ******************************************************************
* socket handling *
****************************************************************** */
int init_socket (int port)
{
SOCKET s;
char *opt;
char hostname[MAX_HOSTNAME + 1];
struct sockaddr_in sa;
struct hostent *hp;
struct linger ld;
bzero (&sa, sizeof (struct sockaddr_in));
gethostname (hostname, MAX_HOSTNAME);
hp = gethostbyname (hostname);
if (hp == NULL) {
perror ("gethostbyname");
WIN32CLEANUP
exit (1);
}
sa.sin_family = hp->h_addrtype;
sa.sin_port = htons ((unsigned short) port);
s = socket (AF_INET, SOCK_STREAM, 0);
if (s == INVALID_SOCKET) {
perror ("Init-socket");
WIN32CLEANUP
exit (1);
}
if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR,
(char *) &opt, sizeof (opt)) < 0) {
perror ("setsockopt REUSEADDR");
WIN32CLEANUP
exit (1);
}
ld.l_onoff = 1;
ld.l_linger = 1000;
if (setsockopt (s, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof (ld)) < 0) {
perror ("setsockopt LINGER");
WIN32CLEANUP
exit (1);
}
if (bind (s, (struct sockaddr *) &sa, sizeof (sa)) < 0) {
perror ("bind");
close (s);
WIN32CLEANUP
exit (1);
}
listen (s, 3);
return (s);
}
int new_connection (SOCKET s)
{
struct sockaddr_in isa;
/* struct sockaddr peer; */
#ifdef WIN32
int i;
#else
socklen_t i;
#endif
SOCKET t;
char buf[100];
i = sizeof (isa);
getsockname (s, (struct sockaddr *) &isa, & i);
if ((t = accept (s, (struct sockaddr *) &isa, &i)) == INVALID_SOCKET) {
perror ("Accept");
return (-1);
}
nonblock (t);
/*
i = sizeof(peer);
if (!getpeername(t, &peer, &i))
{
*(peer.sa_data + 49) = '\0';
sprintf(buf, "New connection from addr %s.\n", peer.sa_data);
log(buf);
}
*/
return (t);
}
int new_descriptor (SOCKET s)
{
int desc;
struct descriptor_data *newd;
#ifdef WIN32
int size;
#else
socklen_t size;
#endif
struct sockaddr_in sock;
struct hostent *from;
char buf[10];
if ((desc = new_connection (s)) < 0)
return (-1);
if (wizlock) {
write_to_descriptor (desc, "The game is wizlocked...");
close (desc);
return (0);
}
#ifdef WIN32
if ((maxdesc + 1) >= avail_descs) {
#else
if ((desc + 1) >= avail_descs) {
#endif
write_to_descriptor (desc, "Sorry.. The game is full...\n\r");
close (desc);
return (0);
#ifdef WIN32
} else
maxdesc++;
#else
} else if (desc > maxdesc)
maxdesc = desc;
#endif
CREATE (newd, struct descriptor_data, 1);
/* find info */
size = sizeof (sock);
if (getpeername (desc, (struct sockaddr *) &sock, &size) < 0) {
perror ("getpeername");
*newd->host = '\0';
} else if (!(from = gethostbyaddr ((char *) &sock.sin_addr,
sizeof (sock.sin_addr), AF_INET))) {
strcpy (newd->host, inet_ntoa (sock.sin_addr));
} else {
strncpy (newd->host, from->h_name, 49);
*(newd->host + 49) = '\0';
}
/* init desc data */
newd->descriptor = desc;
newd->connected = 1;
newd->wait = 1;
newd->prompt_mode = 0;
*newd->buf = '\0';
newd->str = 0;
newd->showstr_head = 0;
newd->showstr_point = 0;
*newd->last_input = '\0';
newd->output.head = NULL;
newd->input.head = NULL;
newd->next = descriptor_list;
newd->character = 0;
newd->original = 0;
newd->snoop.snooping = 0;
newd->snoop.snoop_by = 0;
/* prepend to list */
descriptor_list = newd;
SEND_TO_Q (GREETINGS, newd);
SEND_TO_Q ("By what name do you wish to be known? ", newd);
return (0);
}
int process_output (struct descriptor_data *t)
{
char i[MAX_STRING_LENGTH + 1];
if (!t->prompt_mode && !t->connected)
if (write_to_descriptor (t->descriptor, "\n\r") < 0)
return (-1);
/* Cycle thru output queue */
while (get_from_q (&t->output, i)) {
if (t->snoop.snoop_by) {
write_to_q ("% ", &t->snoop.snoop_by->desc->output);
write_to_q (i, &t->snoop.snoop_by->desc->output);
}
if (write_to_descriptor (t->descriptor, i))
return (-1);
}
if (!t->connected && !(t->character && !IS_NPC (t->character) &&
IS_SET (t->character->specials.act, PLR_COMPACT)))
if (write_to_descriptor (t->descriptor, "\n\r") < 0)
return (-1);
return (1);
}
int write_to_descriptor (int desc, char *txt)
{
int sofar, thisround, total;
total = strlen (txt);
sofar = 0;
do {
thisround = send (desc, txt + sofar, total - sofar, 0);
if (thisround < 0) {
perror ("Write to socket");
return (-1);
}
sofar += thisround;
}
while (sofar < total);
return (0);
}
int process_input (struct descriptor_data *t)
{
int sofar, thisround, begin, squelch, i, k, flag;
char tmp[MAX_INPUT_LENGTH + 2], buffer[MAX_INPUT_LENGTH + 60];
sofar = 0;
flag = 0;
begin = strlen (t->buf);
/* Read in some stuff */
do {
if ((thisround = recv (t->descriptor, t->buf + begin + sofar,
MAX_STRING_LENGTH - (begin + sofar) - 1, 0)) > 0)
sofar += thisround;
else if (thisround < 0)
if (GETERROR != EWOULDBLOCK) {
perror ("Read1 - ERROR");
return (-1);
} else
break;
else {
log ("EOF encountered on socket read.");
return (-1);
}
}
while (!ISNEWL (*(t->buf + begin + sofar - 1)));
*(t->buf + begin + sofar) = 0;
/* if no newline is contained in input, return without proc'ing */
for (i = begin; !ISNEWL (*(t->buf + i)); i++)
if (!*(t->buf + i))
return (0);
/* input contains 1 or more newlines; process the stuff */
for (i = 0, k = 0; *(t->buf + i);) {
if (!ISNEWL (*(t->buf + i)) && !(flag = (k >= (MAX_INPUT_LENGTH - 2))))
if (*(t->buf + i) == '\b') /* backspace */
if (k) { /* more than one char ? */
if (*(tmp + --k) == '$')
k--;
i++;
} else
i++; /* no or just one char.. Skip backsp */
else if (isascii (*(t->buf + i)) && isprint ((int)*(t->buf + i))) {
/* trans char, double for '$' (printf) */
if ((*(tmp + k) = *(t->buf + i)) == '$')
*(tmp + ++k) = '$';
k++;
i++;
} else
i++;
else {
*(tmp + k) = 0;
if (*tmp == '!')
strcpy (tmp, t->last_input);
else
strcpy (t->last_input, tmp);
write_to_q (tmp, &t->input);
if (t->snoop.snoop_by) {
write_to_q ("% ", &t->snoop.snoop_by->desc->output);
write_to_q (tmp, &t->snoop.snoop_by->desc->output);
write_to_q ("\n\r", &t->snoop.snoop_by->desc->output);
}
if (flag) {
sprintf (buffer, "Line too long. Truncated to:\n\r%s\n\r", tmp);
if (write_to_descriptor (t->descriptor, buffer) < 0)
return (-1);
/* skip the rest of the line */
for (; !ISNEWL (*(t->buf + i)); i++);
}
/* find end of entry */
for (; ISNEWL (*(t->buf + i)); i++);
/* squelch the entry from the buffer */
for (squelch = 0;; squelch++)
if ((*(t->buf + squelch) = *(t->buf + i + squelch)) == '\0')
break;
k = 0;
i = 0;
}
}
return (1);
}
void close_sockets (int s)
{
log ("Closing all sockets.");
while (descriptor_list)
close_socket (descriptor_list);
close (s);
}
void close_socket (struct descriptor_data *d)
{
struct affected_type *af;
struct descriptor_data *tmp;
char buf[100];
close (d->descriptor);
flush_queues (d);
#ifndef WIN32
if (d->descriptor == maxdesc)
#endif
--maxdesc;
/* Forget snooping */
if (d->snoop.snooping)
d->snoop.snooping->desc->snoop.snoop_by = 0;
if (d->snoop.snoop_by) {
send_to_char ("Your victim is no longer among us.\n\r",
d->snoop.snoop_by);
d->snoop.snoop_by->desc->snoop.snooping = 0;
}
if (d->character)
if (d->connected == CON_PLYNG) {
save_char (d->character, NOWHERE);
act ("$n has lost $s link.", TRUE, d->character, 0, 0, TO_ROOM);
sprintf (buf, "Closing link to: %s.", GET_NAME (d->character));
log (buf);
d->character->desc = 0;
} else {
sprintf (buf, "Losing player: %s.", GET_NAME (d->character));
log (buf);
free_char (d->character);
} else
log ("Losing descriptor without char.");
if (next_to_process == d) /* to avoid crashing the process loop */
next_to_process = next_to_process->next;
if (d == descriptor_list) /* this is the head of the list */
descriptor_list = descriptor_list->next;
else { /* This is somewhere inside the list */
/* Locate the previous element */
for (tmp = descriptor_list; (tmp->next != d) && tmp; tmp = tmp->next);
tmp->next = d->next;
}
if (d->showstr_head)
free (d->showstr_head);
free (d);
}
void nonblock (SOCKET s)
{
#ifdef WIN32
unsigned long flags = 1;
if (ioctlsocket (s, FIONBIO, &flags)) {
#else
if (fcntl (s, F_SETFL, FNDELAY) == -1) {
#endif
perror ("Noblock");
WIN32CLEANUP
exit (1);
}
}
#define COMA_SIGN \
"\n\r \
DikuMUD is currently inactive due to excessive load on the host machine.\n\r \
Please try again later.\n\r \
\n\r \
Sadly,\n\r \
\n\r \
the DikuMUD system operators\n\r\n\r"
/* sleep while the load is too high */
void coma (SOCKET s)
{
fd_set input_set;
static struct timeval timeout = {
60,
0
};
int conn;
int workhours (void);
int load (void);
log ("Entering comatose state.");
block_signals();
while (descriptor_list)
close_socket (descriptor_list);
FD_ZERO (&input_set);
do {
FD_SET (s, &input_set);
if (select (64, &input_set, 0, 0, &timeout) < 0) {
perror ("coma select");
WIN32CLEANUP
exit (1);
}
if (FD_ISSET (s, &input_set)) {
if (load () < 6) {
log ("Leaving coma with visitor.");
restore_signals();
return;
}
if ((conn = new_connection (s)) >= 0) {
write_to_descriptor (conn, COMA_SIGN);
#if defined WIN32
Sleep (2000);
#else
sleep (2);
#endif
close (conn);
}
}
tics = 1;
if (workhours ()) {
log ("Working hours collision during coma. Exit.");
WIN32CLEANUP
exit (0);
}
}
while (load () >= 6);
log ("Leaving coma.");
restore_signals();
}
/* ****************************************************************
* Public routines for system-to-player-communication *
**************************************************************** */
void send_to_char (char *messg, struct char_data *ch)
{
if (ch->desc && messg)
write_to_q (messg, &ch->desc->output);
}
void send_to_all (char *messg)
{
struct descriptor_data *i;
if (messg)
for (i = descriptor_list; i; i = i->next)
if (!i->connected)
write_to_q (messg, &i->output);
}
void send_to_outdoor (char *messg)
{
struct descriptor_data *i;
if (messg)
for (i = descriptor_list; i; i = i->next)
if (!i->connected)
if (OUTSIDE (i->character))
write_to_q (messg, &i->output);
}
void send_to_except (char *messg, struct char_data *ch)
{
struct descriptor_data *i;
if (messg)
for (i = descriptor_list; i; i = i->next)
if (ch->desc != i && !i->connected)
write_to_q (messg, &i->output);
}
void send_to_room (char *messg, int room)
{
struct char_data *i;
if (messg)
for (i = world[room].people; i; i = i->next_in_room)
if (i->desc)
write_to_q (messg, &i->desc->output);
}
void send_to_room_except (char *messg, int room, struct char_data *ch)
{
struct char_data *i;
if (messg)
for (i = world[room].people; i; i = i->next_in_room)
if (i != ch && i->desc)
write_to_q (messg, &i->desc->output);
}
void send_to_room_except_two
(char *messg, int room, struct char_data *ch1, struct char_data *ch2) {
struct char_data *i;
if (messg)
for (i = world[room].people; i; i = i->next_in_room)
if (i != ch1 && i != ch2 && i->desc)
write_to_q (messg, &i->desc->output);
}
/* higher-level communication */
void act (char *str, int hide_invisible, struct char_data *ch,
struct obj_data *obj, void *vict_obj, int type)
{
register char *strp, *point, *i = NULL;
struct char_data *to;
char buf[MAX_STRING_LENGTH];
if (!str)
return;
if (!*str)
return;
if (type == TO_VICT)
to = (struct char_data *) vict_obj;
else if (type == TO_CHAR)
to = ch;
else
to = world[ch->in_room].people;
for (; to; to = to->next_in_room) {
if (to->desc && ((to != ch) || (type == TO_CHAR)) &&
(CAN_SEE (to, ch) || !hide_invisible) && AWAKE (to) &&
!((type == TO_NOTVICT) && (to == (struct char_data *) vict_obj))) {
for (strp = str, point = buf;;)
if (*strp == '$') {
switch (*(++strp)) {
case 'n':
i = PERS (ch, to);
break;
case 'N':
i = PERS ((struct char_data *) vict_obj, to);
break;
case 'm':
i = HMHR (ch);
break;
case 'M':
i = HMHR ((struct char_data *) vict_obj);
break;
case 's':
i = HSHR (ch);
break;
case 'S':
i = HSHR ((struct char_data *) vict_obj);
break;
case 'e':
i = HSSH (ch);
break;
case 'E':
i = HSSH ((struct char_data *) vict_obj);
break;
case 'o':
i = OBJN (obj, to);
break;
case 'O':
i = OBJN ((struct obj_data *) vict_obj, to);
break;
case 'p':
i = OBJS (obj, to);
break;
case 'P':
i = OBJS ((struct obj_data *) vict_obj, to);
break;
case 'a':
i = SANA (obj);
break;
case 'A':
i = SANA ((struct obj_data *) vict_obj);
break;
case 'T':
i = (char *) vict_obj;
break;
case 'F':
i = fname ((char *) vict_obj);
break;
case '$':
i = "$";
break;
default:
log ("Illegal $-code to act():");
log (str);
break;
}
while (*point = *(i++))
++point;
++strp;
} else if (!(*(point++) = *(strp++)))
break;
*(--point) = '\n';
*(++point) = '\r';
*(++point) = '\0';
write_to_q (CAP (buf), &to->desc->output);
}
if ((type == TO_VICT) || (type == TO_CHAR))
return;
}
}