/************************************************************************
Realms of Aurealis James Rhone aka Vall of RoA
comm.c Heavily based on comm.c from CircleMUD3.0.
Additions include external editor code
along with all the functions associated
with it. RoAOLC also needs some mods to
this file with roaolc_menu checks thru-
out.
5/1/97 - Heavy mods to allow communication with first
generation RoAClients. -jtrhone
5/19/98 - more mods have been added and mostly moved to
clicomm.c -jtrhone
******** Heavily modified and expanded ********
*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
******** Heavily modified and expanded ********
All rights reserved henceforth.
Please note that no guarantees are associated with any code from
Realms of Aurealis. All code which has been released to the general
public has been done so with an 'as is' pretense. RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license. *** Read, Learn, Understand, Improve ***
*************************************************************************/
#define __COMM_C__
#include "sysdep.h"
#include "conf.h"
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <arpa/telnet.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "structures.h"
#include "utils.h"
#include "comm.h"
#include "clicomm.h"
#include "interpreter.h"
#include "acmd.h"
#include "handler.h"
#include "db.h"
#include "house.h"
#include "identd/ident.h"
#include "screen.h"
#include "roaolc.h"
#include "lists.h"
#include "global.h"
#include "plshop.h"
#include "darkenelf.h"
#include "descmenu.h"
/* externs */
extern int restrict;
extern int mini_mud;
extern FILE *player_fl;
extern int DFLT_PORT;
extern char *DFLT_DIR;
/* local globals */
dsdata *descriptor_list = NULL; /* master desc list of normal descriptors */
dsdata *cdesc_list = NULL; /* master desc list of client descriptors */
cldesc *prelim_list = NULL; /* global list of preliminary clients... */
struct txt_block *bufpool = 0; /* pool of large output buffers */
int buf_largecount = 0; /* # of large buffers which exist */
int buf_overflows = 0; /* # of overflows of output */
int buf_switches = 0; /* # of switches from small to large buf */
int circle_shutdown = 0; /* clean shutdown */
int circle_reboot = 0; /* reboot the game after a shutdown */
int max_players = 0; /* max descriptors available */
int tics = 0; /* for extern checkpointing */
struct timeval null_time; /* zero-valued time structure */
int MASTER_DESCRIPTOR; /* assigned to mother_desc for exshell.c */
int CLIENT_DESCRIPTOR; /* assigned to client_desc for exshell.c */
/* internal functions */
int get_from_q(struct txt_q *queue, char *dest, int *aliased);
void init_game(int port);
void setup_checkpoint(void);
void signal_setup(void);
void game_loop(int mother_desc, int client_desc);
void respond_to_input(void);
int init_socket(int port);
int new_descriptor(int s);
int get_max_players(void);
int process_output(dsdata *t);
int process_input(dsdata *t);
void close_socket(dsdata *d);
void flush_queues(dsdata *d);
void nonblock(int s);
void block(int s);
int perform_subst(dsdata *t, char *orig, char *subst);
int perform_alias(dsdata *d, char *orig);
void record_usage(void);
void make_prompt(dsdata *point);
void check_idle_passwords(void);
void send_auction(char *messg);
struct timeval timediff(struct timeval *a, struct timeval *b);
void update_vt100_bars(void);
void handle_heartbeat_stuff(int *real_pulse);
/* extern fcnts */
void boot_db(void);
void boot_world(void);
void zone_update(void);
void affect_update(void); /* In spells.c */
void point_update(void); /* In limits.c */
void mobile_activity(BOOL doubletime);
void magical_activity(void);
void perform_violence(void);
void update_client_stats(BOOL combat);
void show_string(dsdata *d, char *input);
int isbanned(char *hostname);
void another_hour(void);
void string_add(dsdata *d, char *str, BOOL term);
extern void jump_to_ritual(chdata *ch);
extern void rand_rite_messg(chdata *p);
extern int update_who_html(void);
extern int update_stats_html(void);
extern void update_goss_html(void);
extern void do_auction_update(void);
extern void room_activity(BOOL doubletime);
extern void zone_activity(void);
extern void info_broadcast(void);
extern void check_arena_state(BOOL award);
extern void free_the_mud(void);
extern void do_arena_update(void);
extern void mortal_save(void);
extern void save_board_config(void);
// MUST be linked up to the libdesc library to use this -roa
ACMD(do_identify)
{
chdata *vict;
IDENT *idinfo;
char *argu = argument;
skip_spaces(&argu);
if (!(vict = get_char_vis(ch, argu)))
{
send_to_char("Nobody by that name around.\n\r",ch);
return;
}
if (!vict->desc)
{
send_to_char("There is no link to identify.\n\r",ch);
return;
}
idinfo = ident_lookup(vict->desc->descriptor, 5);
sprintf(buf, "%%6Identity information on %%B%s%%0:\n\r", GET_NAME(vict));
S2C();
if (!idinfo)
{
send_to_char("Remote host not running identd or connection timed out.\n\r",ch);
return;
}
sprintf(buf, " %%5Username%%0: %s\n\r", idinfo->identifier);
S2C();
sprintf(buf, " %%5Host%%0: %s\n\r", vict->desc->host);
S2C();
sprintf(buf, " %%5Remote OS%%0: %s\n\r", idinfo->opsys);
S2C();
sprintf(buf, " %%5Remote CharSet%%0: %s\n\r", idinfo->charset);
S2C();
sprintf(buf, " %%5Lport%%0: %d\n\r", idinfo->lport);
S2C();
sprintf(buf, " %%5Fport%%0: %d\n\r", idinfo->fport);
S2C();
// fixed mem leak here...7/21/98 -jtrhone
ident_free(idinfo);
}
/* *********************************************************************
* 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 '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(1);
}
break;
case 'm':
mini_mud = TRUE;
log("Running in minimized mode.");
break;
case 'r':
restrict = 1;
log("Restricting mud -- no new players allowed (wizlock 1).");
break;
case 'w':
restrict = 71;
log("Restricting mud -- no mortals allowed (wizlock 71).");
break;
default:
sprintf(buf, "SYSERR: Unknown option -%c in argument string.", *(argv[pos] + 1));
log(buf);
break;
}
pos++;
}
if (pos < argc) {
if (!isdigit(*argv[pos])) {
fprintf(stderr, "Usage: %s [-c] [-m] [-q] [-r] [-d pathname] [port #]\n", argv[0]);
exit(1);
} else if ((port = atoi(argv[pos])) <= 1024) {
fprintf(stderr, "Illegal port number.\n");
exit(1);
}
}
if (chdir(dir) < 0) {
perror("Fatal error changing to data directory");
exit(1);
}
sprintf(buf, "Using %s as data directory.", dir);
log(buf);
sprintf(buf, "Main Server on port %d.", port);
log(buf);
init_game(port);
return 0;
}
/* Init sockets, run game, and cleanup sockets */
void init_game(int port)
{
int mother_desc;
int client_desc;
srandom(time(0));
// make sure we run with these ENV vars set right... 2/12/98 -jtrhone
log("Initializing environment.");
system("rm -f misc/*.soklock >& /dev/null");
putenv("TERM=vt100");
log("Opening mother connection.");
mother_desc = init_socket(port);
// this will also now fork off a cli_router... for client connections...
client_desc = init_client_socket(port + 1);
/* testing jtrhone -roa*/
MASTER_DESCRIPTOR = mother_desc;
CLIENT_DESCRIPTOR = client_desc;
max_players = get_max_players();
boot_db();
log("Signal trapping.");
signal_setup();
log("Entering game loop.");
game_loop(mother_desc, client_desc);
log("Closing all mother connections.");
while (descriptor_list)
close_socket(descriptor_list);
close(mother_desc);
close(client_desc);
fclose(player_fl);
// ignore SIGCHILD on shutdown
signal(SIGCHLD, SIG_IGN);
// if we brought one up, kill it...
if (cli_router_pid > 0)
kill(cli_router_pid, SIGTERM);
// now remove local socket 5/19/98 -jtrhone
sprintf(buf, "Removing %s.", local_sockname);
log(buf);
unlink(local_sockname);
// cleanup...
system("rm -f misc/*.soklock >& /dev/null");
if (circle_reboot) {
log("Rebooting.");
exit(52); /* what's so great about HHGTTG, anyhow? */
}
log("Normal termination of game.");
}
/*
* init_socket sets up the mother descriptor - creates the socket, sets
* its options up, binds it, and listens.
*/
int init_socket(int port)
{
int s, opt;
struct sockaddr_in sa;
/*
* Should the first argument to socket() be AF_INET or PF_INET? I don't
* know, take your pick. PF_INET seems to be more widely adopted, and
* Comer (_Internetworking with TCP/IP_) even makes a point to say that
* people erroneously use AF_INET with socket() when they should be using
* PF_INET. However, the man pages of some systems indicate that AF_INET
* is correct; some such as ConvexOS even say that you can use either one.
* All implementations I've seen define AF_INET and PF_INET to be the same
* number anyway, so ths point is (hopefully) moot.
*/
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("Create socket");
exit(1);
}
#if defined(SO_SNDBUF)
opt = LARGE_BUFSIZE + GARBAGE_SPACE;
if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) {
perror("setsockopt SNDBUF");
exit(1);
}
#endif
#if defined(SO_REUSEADDR)
opt = 1;
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) {
perror("setsockopt REUSEADDR");
exit(1);
}
#endif
#if defined(SO_LINGER)
{
struct linger ld;
ld.l_onoff = 0;
ld.l_linger = 0;
if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)) < 0) {
perror("setsockopt LINGER");
exit(1);
}
}
#endif
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
perror("bind");
close(s);
exit(1);
}
nonblock(s);
listen(s, 5);
return s;
}
int get_max_players(void)
{
int max_descs = 0;
char *method;
#ifdef OS2
return MAX_PLAYERS;
#else
/*
* First, we'll try using getrlimit/setrlimit. This will probably work
* on most systems.
*/
#if defined (RLIMIT_NOFILE) || defined (RLIMIT_OFILE)
#if !defined(RLIMIT_NOFILE)
#define RLIMIT_NOFILE RLIMIT_OFILE
#endif
{
struct rlimit limit;
/* find the limit of file descs */
method = "rlimit";
if (getrlimit(RLIMIT_NOFILE, &limit) < 0) {
perror("calling getrlimit");
exit(1);
}
/* set the current to the maximum */
limit.rlim_cur = limit.rlim_max;
if (setrlimit(RLIMIT_NOFILE, &limit) < 0) {
perror("calling setrlimit");
exit(1);
}
#ifdef RLIM_INFINITY
if (limit.rlim_max == RLIM_INFINITY)
max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS;
else
max_descs = MIN(MAX_PLAYERS + NUM_RESERVED_DESCS, limit.rlim_max);
#else
max_descs = MIN(MAX_PLAYERS + NUM_RESERVED_DESCS, limit.rlim_max);
#endif
}
#elif defined (OPEN_MAX) || defined(FOPEN_MAX)
#if !defined(OPEN_MAX)
#define OPEN_MAX FOPEN_MAX
#endif
method = "OPEN_MAX";
max_descs = OPEN_MAX; /* Uh oh.. rlimit didn't work, but we have
* OPEN_MAX */
#elif defined (POSIX)
/*
* Okay, you don't have getrlimit() and you don't have OPEN_MAX. Time to
* use the POSIX sysconf() function. (See Stevens' _Advanced Programming
* in the UNIX Environment_).
*/
method = "POSIX sysconf";
errno = 0;
if ((max_descs = sysconf(_SC_OPEN_MAX)) < 0) {
if (errno == 0)
max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS;
else {
perror("Error calling sysconf");
exit(1);
}
}
#else
/* if everything has failed, we'll just take a guess */
max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS;
#endif
/* now calculate max _players_ based on max descs */
max_descs = MIN(MAX_PLAYERS, max_descs - NUM_RESERVED_DESCS);
if (max_descs <= 0) {
sprintf(buf, "Non-positive max player limit! (Set at %d using %s).", max_descs, method);
log(buf);
exit(1);
}
sprintf(buf, "Setting player limit to %d using %s.", max_descs, method);
log(buf);
return max_descs;
#endif /* OS2 */
}
// Now handle second list of connections, from client descriptor
// use the same select, accept new client connections as well now
// as well as poll thru available client connections and
// parsing thru client headers -RoA
// now select on list of cldescs (preliminary clients before login) as well 5/20/98 -jtrhone
void game_loop(int mother_desc, int client_desc)
{
fd_set input_set, output_set, exc_set;
struct timeval last_time, now, timespent, timeout, opt_time;
dsdata *d, *next_d;
cldesc *c, *next_c;
int pulse = 0, maxdesc;
/* initialize various time values */
null_time.tv_sec = 0;
null_time.tv_usec = 0;
opt_time.tv_usec = OPT_USEC;
opt_time.tv_sec = 0;
gettimeofday(&last_time, (struct timezone *) 0);
while (!circle_shutdown)
{
/* Sleep if we don't have any connections */
if (!descriptor_list && !cdesc_list && !prelim_list)
{
log("No connections. Going to sleep.");
// null out entire input set (read_set) -roa
FD_ZERO(&input_set);
// stick our mother into the input set -roa
FD_SET(mother_desc, &input_set);
// stick client in there too, but mother should always be trigged
// first, just a safety check i guess if mother is plugged
FD_SET(client_desc, &input_set);
if (select(MAX(mother_desc, client_desc)+1, &input_set, (fd_set *) 0, (fd_set *) 0, NULL) < 0)
{
if (errno == EINTR)
log("Waking up to process signal.");
else
perror("Select coma");
} else
log("New connection. Waking up.");
gettimeofday(&last_time, (struct timezone *) 0);
}
/* Set up the input, output, and exception sets for select(). */
// clear out every set, init them to NULL -roa
FD_ZERO(&input_set);
FD_ZERO(&output_set);
FD_ZERO(&exc_set);
// put our mother, client_desc into the input set
FD_SET(mother_desc, &input_set);
FD_SET(client_desc, &input_set);
// high end of the descriptors we scan starts at mother_desc -roa
// in theory, client is higher, but it could have rolled
maxdesc = MAX(mother_desc, client_desc);
// now, go thru list, find the high end of the range of descriptors
// and stick each of them into our read,write,and exception sets -roa
Descriptors(d)
{
if (d->descriptor > maxdesc)
maxdesc = d->descriptor;
FD_SET(d->descriptor, &input_set);
FD_SET(d->descriptor, &output_set);
FD_SET(d->descriptor, &exc_set);
}
// now scan RoA client descriptor list and insert
for (d = cdesc_list; d; d = d->next_client)
{
if (d->cdesc > maxdesc)
maxdesc = d->cdesc;
FD_SET(d->cdesc, &input_set);
FD_SET(d->cdesc, &output_set);
FD_SET(d->cdesc, &exc_set);
}
// now, go thru cldesc list as well, lookin for max... 5/20/98 -jtrhone
for (c = prelim_list; c; c = c->next)
{
if (c->desc > maxdesc)
maxdesc = c->desc;
FD_SET(c->desc, &input_set);
FD_SET(c->desc, &output_set);
FD_SET(c->desc, &exc_set);
}
do {
errno = 0; /* clear error condition */
/* figure out for how long we have to sleep */
gettimeofday(&now, (struct timezone *) 0);
timespent = timediff(&now, &last_time);
timeout = timediff(&opt_time, ×pent);
/* sleep (regardless!) until the next 0.1 second tick */
if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0)
if (errno != EINTR) {
perror("Select sleep");
exit(1);
}
} while (errno);
/* record the time for the next pass */
gettimeofday(&last_time, (struct timezone *) 0);
/* poll (without blocking) for new input, output, and exceptions */
// we are scanning the i,o,e sets for changes in their state
// those sets contain every player descriptor in the game including mother
if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) < 0)
{
perror("Select poll");
return;
}
/* If there are new connections waiting, accept them. */
// ok, mama is in the input_set, someone is knockin at her door -roa
if (FD_ISSET(mother_desc, &input_set))
new_descriptor(mother_desc);
// client monitor has a new connection, go getem one
// no longer insert into client list, insert now into prelim list
// upon successful process_prelim_input and login, this desc will be
// put into client list
if (FD_ISSET(client_desc, &input_set))
new_prelim_socket(client_desc);
// anybody in the exception set, we wanna rip from the game
// and remove them from the input and output sets -roa
for (d = descriptor_list; d; d = next_d)
{
next_d = d->next;
if (FD_ISSET(d->descriptor, &exc_set))
{
FD_CLR(d->descriptor, &input_set);
FD_CLR(d->descriptor, &output_set);
close_socket(d);
}
}
// kick client exceptions out
for (d = cdesc_list; d; d = next_d)
{
next_d = d->next_client;
if (FD_ISSET(d->cdesc, &exc_set))
{
FD_CLR(d->cdesc, &input_set);
FD_CLR(d->cdesc, &output_set);
close_client_socket(d);
}
}
// kick preliminary client exceptions out 5/20/98 -jtrhone
for (c = prelim_list; c; c = next_c)
{
next_c = c->next;
if (FD_ISSET(c->desc, &exc_set))
{
FD_CLR(c->desc, &input_set);
FD_CLR(c->desc, &output_set);
close_prelim_socket(c);
}
}
// process all descriptors which are in our input_set
for (d = descriptor_list; d; d = next_d)
{
next_d = d->next;
if (FD_ISSET(d->descriptor, &input_set))
if (process_input(d) < 0)
close_socket(d);
}
// take care of client input
for (d = cdesc_list; d; d = next_d)
{
next_d = d->next_client;
if (FD_ISSET(d->cdesc, &input_set))
if (process_client_input(d) < 0)
close_client_socket(d);
}
// take care of prelim client input
for (c = prelim_list; c; c = next_c)
{
next_c = c->next;
if (FD_ISSET(c->desc, &input_set))
if (process_prelim_client(c) < 0)
close_prelim_socket(c);
}
// now each descriptor has had its input stuck onto a queue
// lets go thru and yank a chunk of each players queue and
// decipher it and process it
respond_to_input();
/* send queued output out to the operating system (ultimately to user) */
// ok, now take care of those descriptors in our output_set -roa
for (d = descriptor_list; d; d = next_d)
{
next_d = d->next;
if (FD_ISSET(d->descriptor, &output_set) && *(d->output))
if (process_output(d) < 0)
close_socket(d);
else
d->prompt_mode = 1;
}
// now process output from mud to client
// doesn't do much since client output isn't buffered (yet)...
for (d = cdesc_list; d; d = next_d)
{
next_d = d->next_client;
if (FD_ISSET(d->cdesc, &output_set))
if (process_client_output(d) < 0)
close_client_socket(d);
}
// wax CON_CLOSEd folks, this will also wax client connections
for (d = descriptor_list; d; d = next_d) {
next_d = d->next;
if (STATE(d) == CON_CLOSE)
close_socket(d);
}
/* give each descriptor an appropriate prompt */
Descriptors(d)
if (d->prompt_mode)
{
make_prompt(d);
d->prompt_mode = 0;
}
// Note: pulse only updates every 0.10 seconds because we force
// the mud to sleep for that amount of time with the select statement
// above as we send it NULL i,o,e sets and a timeval of 0.10 secs -roa
pulse++;
handle_heartbeat_stuff(&pulse);
tics++; /* tics since last checkpoint signal */
}
// if we have logged, close the file ptr 5/13/98 -jtrhone
if (memfp)
fclose(memfp);
// ok, were out of the main loop, ready to shutdown
// lets free a ton of stuff so we can track down memory leaks
// with the dmalloc libraries easier -roa
free_the_mud();
#ifndef DMALLOC_DISABLE
#ifdef DEBUG_MAX
dmalloc_log_stats();
dmalloc_log_unfreed();
#endif
#endif
}
// no more nanny call or STATE() macros..., if they in a descriptor menu, call it 7/14/98 -jtrhone
void respond_to_input(void)
{
dsdata *d, *next_d;
chdata *player;
char comm[MAX_INPUT_LENGTH];
char combuf[256];
int aliased;
for (d = descriptor_list; d; d = next_d)
{
next_d = d->next;
player = d->character;
// check for arena and ritualistic things first
if (D_CHECK(d) && IS_PC(player))
{
if (IN_ARENA(player) && !ZONE_FLAGGED(world[player->in_room].zone, Z_ARENA))
{
REMOVE_BIT(PLR_FLAGS(player), PLR_ARENA);
check_arena_state(TRUE);
}
if (IN_A_RITUAL(player) && DELAY_TYPE(player))
{
if (!(d->wait % 60)) /* every 60 pulses show messg */
rand_rite_messg(player);
if ((--d->wait) <= 0) /* ready to finish ritual? */
{
d->wait = 1;
jump_to_ritual(player);
}
continue; // dont take input from this guy yet
}
}
aliased = FALSE;
if ((--(d->wait) <= 0) && get_from_q(&d->input, comm, &aliased))
{
// Record time of last entered command... 03/21/98 -callahan
d->last_time = time(0);
if (d->character)
{
/* pull char back from void if necessary */
d->character->specials.timer = 0;
if (!d->connected && GET_WAS_IN(d->character) != NOWHERE)
{
if (!IN_NOWHERE(d->character))
char_from_room(d->character);
char_to_room(d->character, GET_WAS_IN(d->character));
GET_WAS_IN(d->character) = NOWHERE;
act("$n has returned.", TRUE, d->character, 0, 0, TO_ROOM);
}
}
d->wait = 1;
d->prompt_mode = 1;
// if they are rerouted... we basically ignore them...
if (d->rerouted)
{
if (d->character)
sprintf(combuf, "SYSUPD: %s rerouted input in comm.c", GET_NAME(d->character));
else
sprintf(combuf, "SYSUPD: Unknown char rerouted in comm.c.");
mudlog(combuf, BUG, LEV_IMM, TRUE);
continue;
}
else
if (d->str) /* writing boards, mail, etc. */
string_add(d, comm, FALSE);
else
if (d->showstr_count) /* reading something w/ pager */
show_string(d, comm);
else
if (d->descmenu) /* in descmenus */
{
d->idle_tics = 0;
(*d->descmenu)(d, comm);
}
else
if (d->roaolc_menu) /* in RoA OLC */
{
if (d->character)
(*d->roaolc_menu)(d->character, comm);
else
mudlog("SYSERR: NonCharacter menu access in comm.c", BRF,LEV_IMM,TRUE);
}
else
{ /* else: we're playing normally */
if (aliased) /* to prevent recursive aliases */
d->prompt_mode = 0;
else
{
/* run it through aliasing system */
if (d->character)
{
if (perform_alias(d, comm))
get_from_q(&d->input, comm, &aliased);
}
else
{
mudlog("SYSERR: NonCharacter perform_alias attempt in comm.c", BRF,LEV_IMM,TRUE);
}
}
/* send it to interpreter */
command_interpreter(d->character, comm);
if (VT100(d->character))
updatescr(d->character);
}
}
}
}
// updated room_activity for rproc dtime 6/6/98 -jtrhone
void handle_heartbeat_stuff(int *real_pulse)
{
int pulse = *real_pulse;
static int mins_since_crashsave = 0;
static BOOL dtime = FALSE;
static BOOL rdtime = FALSE;
extern void room_save_all(void);
if (!(pulse % PULSE_PASSWORD))
check_idle_passwords();
if (!(pulse % PULSE_ZONE))
{
zone_update();
zone_activity();
}
if (!(pulse % PULSE_MAGIC))
magical_activity(); // in mobact.c for now -roa
if (!(pulse % PULSE_ROOM))
{
room_activity(rdtime);
rdtime = !rdtime;
}
if (!(pulse % PULSE_MOBILE))
{
mobile_activity(dtime);
dtime = !dtime;
}
// do the normal violence thing
if (!(pulse % PULSE_VIOLENCE))
{
perform_violence();
// for all those players who are vt100 and need an update
update_vt100_bars();
// for all those peeps who WANT stat data every 2 seconds... 8/3/98 -jtrhone
update_client_stats(TRUE);
}
/* do per tick i guess? per hour */
if (!(pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC)))
{
another_hour();
affect_update();
point_update();
fflush(player_fl);
max_on = MAX(max_on, update_who_html());
update_stats_html();
update_goss_html();
do_auction_update();
do_arena_update();
info_broadcast();
// update these every tick, regardless if they fighting 8/4/98 -jtrhone
update_client_stats(FALSE);
}
/* 1 minute */
if (auto_save)
if (!(pulse % (60 * PASSES_PER_SEC)))
if (++mins_since_crashsave >= autosave_time)
{
mins_since_crashsave = 0;
mortal_save();
House_save_all();
room_save_all();
}
/* 5 minutes */
if (!(pulse % (300 * PASSES_PER_SEC)))
record_usage();
/* 30 minutes */
if (pulse >= (30 * 60 * PASSES_PER_SEC))
{
*real_pulse = 0;
// save config files every half hour... -jtrhone 11/25/97
save_configuration();
save_board_config();
save_plshops();
}
}
/* ******************************************************************
* general utility stuff (for local use) *
****************************************************************** */
/*
* new code to calculate time differences, which works on systems
* for which tv_usec is unsigned (and thus comparisons for something
* being < 0 fail). Based on code submitted by ss@sirocco.cup.hp.com.
*/
/*
* code to return the time difference between a and b (a-b).
* always returns a nonnegative value (floors at 0).
*/
struct timeval timediff(struct timeval *a, struct timeval *b)
{
struct timeval rslt;
if (a->tv_sec < b->tv_sec)
return null_time;
else if (a->tv_sec == b->tv_sec) {
if (a->tv_usec < b->tv_usec)
return null_time;
else {
rslt.tv_sec = 0;
rslt.tv_usec = a->tv_usec - b->tv_usec;
return rslt;
}
} else { /* a->tv_sec > b->tv_sec */
rslt.tv_sec = a->tv_sec - b->tv_sec;
if (a->tv_usec < b->tv_usec) {
rslt.tv_usec = a->tv_usec + 1000000 - b->tv_usec;
rslt.tv_sec--;
} else
rslt.tv_usec = a->tv_usec - b->tv_usec;
return rslt;
}
}
void record_usage(void)
{
int sockets_connected = 0, sockets_playing = 0;
dsdata *d;
char buf[256];
Descriptors(d) {
sockets_connected++;
if (!d->connected)
sockets_playing++;
}
sprintf(buf, "nusage: %-3d sockets connected, %-3d sockets playing",
sockets_connected, sockets_playing);
log(buf);
if (0)
{
struct rusage ru;
getrusage(0, &ru);
sprintf(buf, "rusage: user time: %d sec, system time: %d sec, max res size: %ld",
ru.ru_utime.tv_sec, ru.ru_stime.tv_sec, ru.ru_maxrss);
log(buf);
}
}
/*
* Turn off echoing (specific to telnet client)
*/
void echo_off(dsdata *d)
{
char off_string[] =
{
(char) IAC,
(char) WILL,
(char) TELOPT_ECHO,
(char) 0,
};
send_to_q(off_string, d);
}
/*
* Turn on echoing (specific to telnet client)
*/
void echo_on(dsdata *d)
{
char on_string[] =
{
(char) IAC,
(char) WONT,
(char) TELOPT_ECHO,
(char) TELOPT_NAOFFD,
(char) TELOPT_NAOCRD,
(char) 0,
};
send_to_q(on_string, d);
}
// at predetermined interval, go thru descriptor list and updatescr
void update_vt100_bars(void)
{
dsdata *d = descriptor_list;
dsdata *next_d;
for ( ; d; d = next_d)
{
next_d = d->next;
if (D_CHECK(d) && IS_PC(d->character) && VT100(d->character) &&
d->character->pc_specials->needs_update)
updatescr(d->character);
}
}
// give our peeps a prompt if they need one
void make_prompt(dsdata *d)
{
char prompt[MAX_INPUT_LENGTH+1];
// dont give em no prompt if they shelled out -roa
if (d->rerouted)
return;
else
if (d->character && INCHAT(d->character))
{
write_to_descriptor(d->descriptor, ">");
return;
}
else
if (d->showstr_count) // they using the pager type code?
{
sprintf(prompt,
"\r[ Return to continue, (q)uit, (r)efresh, (b)ack, or page number (%d/%d) ]",
d->showstr_page, d->showstr_count);
write_to_descriptor(d->descriptor, prompt);
if (d->character && IS_PC(d->character))
d->character->pc_specials->needs_update = FALSE;
}
else
if (d->descmenu_prompt) // they have descmenu?
{
write_to_descriptor(d->descriptor, d->descmenu_prompt);
if (d->character && IS_PC(d->character))
d->character->pc_specials->needs_update = FALSE;
}
else
if (d->menu_prompt) // they have an RoAOLC menu?
{
write_to_descriptor(d->descriptor, d->menu_prompt);
if (d->character && IS_PC(d->character))
d->character->pc_specials->needs_update = FALSE;
}
else
if (d->str) // they in the old fashioned editor?
{
write_to_descriptor(d->descriptor, "] ");
if (d->character && IS_PC(d->character))
d->character->pc_specials->needs_update = FALSE;
}
else
if (D_CHECK(d) && !VT100(d->character))
{
display_prompt(d->character, prompt);
write_to_descriptor(d->descriptor, prompt);
if (d->character && IS_PC(d->character))
d->character->pc_specials->needs_update = FALSE;
}
}
// throw a chunk of text on a queue
void write_to_q(char *txt, struct txt_q *queue, int aliased)
{
struct txt_block *neww;
CREATE(neww, struct txt_block, 1);
CREATE(neww->text, char, strlen(txt) + 1);
strcpy(neww->text, txt);
neww->aliased = aliased;
/* queue empty? */
if (!queue->head) {
neww->next = NULL;
queue->head = queue->tail = neww;
} else {
queue->tail->next = neww;
queue->tail = neww;
neww->next = NULL;
}
}
/*
used to be a macro, BLAH, i had to throw a thing in here, easier as a
function now -jtrhone
*/
void send_to_q(char *messg, dsdata *d)
{
if (!messg || !*messg) return;
if (d->character && IS_PC(d->character) && VT100(d->character))
d->character->pc_specials->needs_update = TRUE;
write_to_output(messg, d);
}
// yank a chunk off the queue
int get_from_q(struct txt_q *queue, char *dest, int *aliased)
{
struct txt_block *tmp;
/* queue empty? */
if (!queue->head)
return 0;
tmp = queue->head;
strcpy(dest, queue->head->text);
*aliased = queue->head->aliased;
queue->head = queue->head->next;
free(tmp->text);
free(tmp);
return 1;
}
/* Empty the queues before closing connection */
void flush_queues(dsdata *d)
{
int dummy;
if (d->large_outbuf) {
d->large_outbuf->next = bufpool;
bufpool = d->large_outbuf;
}
while (get_from_q(&d->input, buf2, &dummy));
}
/* Add a new string to a players output queue */
void write_to_output(const char *txt, dsdata *t)
{
int size;
size = strlen(txt);
/* if were in the overflow state already, ignore this new output */
if (t->bufptr < 0)
return;
/* if we have enough space, just write to buffer and thats it! */
if (t->bufspace >= size) {
strcpy(t->output + t->bufptr, txt);
t->bufspace -= size;
t->bufptr += size;
return;
}
/*
* If were already using the large buffer, or if even the large buffer
* is too small to handle this new text, chuck the text and switch to the
* overflow state.
*/
if (t->large_outbuf || ((size + strlen(t->output)) > LARGE_BUFSIZE)) {
t->bufptr = -1;
buf_overflows++;
mudlog("SYSUPD: Buf overflow noted.",BUG,LEV_IMM,TRUE);
return;
}
buf_switches++;
/* if the pool has a buffer in it, grab it */
if (bufpool != NULL) {
t->large_outbuf = bufpool;
bufpool = bufpool->next;
} else { /* else create a new one */
CREATE(t->large_outbuf, struct txt_block, 1);
CREATE(t->large_outbuf->text, char, LARGE_BUFSIZE);
buf_largecount++;
}
strcpy(t->large_outbuf->text, t->output); /* copy to big buffer */
t->output = t->large_outbuf->text; /* make big buffer primary */
strcat(t->output, txt); /* now add new text */
/* calculate how much space is left in the buffer */
t->bufspace = LARGE_BUFSIZE - 1 - strlen(t->output);
/* set the pointer for the next write */
t->bufptr = strlen(t->output);
}
/* ******************************************************************
* socket handling *
****************************************************************** */
// set up descriptor, and descriptor struct
int new_descriptor(int s)
{
int desc, sockets_connected = 0, i;
unsigned long addr;
static int last_desc = 0;
dsdata *newd;
struct sockaddr_in peer;
struct hostent *from;
/* accept the new connection */
i = sizeof(peer);
if ((desc = accept(s, (struct sockaddr *) &peer, &i)) < 0) {
perror("accept");
return -1;
}
/* keep it from blocking */
nonblock(desc);
/* make sure we have room for it */
Descriptors(newd)
sockets_connected++;
if (sockets_connected >= max_players)
{
sprintf(buf, "Sorry, %s is full right now... please try again later!\n\r", shortmudname);
write_to_descriptor(desc, buf);
close(desc);
return 0;
}
/* create a new descriptor */
CREATE(newd, dsdata, 1);
memset((char *) newd, 0, sizeof(dsdata));
/* find the sitename */
if (nameserver_is_slow || !(from = gethostbyaddr((char *) &peer.sin_addr, sizeof(peer.sin_addr), AF_INET)))
{
/* resolution failed */
if (!nameserver_is_slow)
perror("gethostbyaddr");
/* find the numeric site address */
addr = ntohl(peer.sin_addr.s_addr);
sprintf(newd->host, "%03u.%03u.%03u.%03u", (int) ((addr & 0xFF000000) >> 24),
(int) ((addr & 0x00FF0000) >> 16), (int) ((addr & 0x0000FF00) >> 8),
(int) ((addr & 0x000000FF)));
} else {
strncpy(newd->host, from->h_name, 30);
*(newd->host + 30) = '\0';
}
/* determine if the site is banned */
if (isbanned(newd->host) == BAN_ALL) {
close(desc);
sprintf(buf2, "Connection attempt denied from [%s]", newd->host);
mudlog(buf2, CMP, LEV_GOD, TRUE);
free(newd);
return 0;
}
/* initialize descriptor data */
newd->descriptor = desc;
newd->pos = -1;
newd->wait = 1;
newd->output = newd->small_outbuf;
newd->bufspace = SMALL_BUFSIZE - 1;
newd->login_time = time(0);
newd->last_time = time(0); // For idle-monitoring... 03/21/98 -callahan
newd->zone_stimer = -1;
newd->room_stimer = -1;
newd->mob_stimer = -1;
STATE(newd) = CON_DESCMENU;
// we need a number to reference this descriptor for wiz commands
// this has nothing to do with the socket or descriptor stuff -roa
if (++last_desc == 1000)
{
last_desc = 1;
mudlog("SYSUPD: 1000 connections reached. Reset to 1.", BUG, LEV_IMM, TRUE);
}
newd->desc_num = last_desc;
/* prepend to our list of descriptors list */
newd->next = descriptor_list;
descriptor_list = newd;
// jump to getname menu in descmenu.c
descmenu_jump(newd, getname);
return 0;
}
// each character has a buffer of output waiting to be processed
// (normally), lets process a chunk and send it out
int process_output(dsdata *t)
{
static char i[LARGE_BUFSIZE + GARBAGE_SPACE];
static int result;
/* we may need this \r\n for later -- see below */
strcpy(i, "\r\n");
/* now, append the 'real' output */
str_cpy(i + 2, t->output, LARGE_BUFSIZE + GARBAGE_SPACE -2, "process_output");
/* if we're in the overflow state, notify the user */
if (t->bufptr < 0)
strcat(i, "**OVERFLOW**");
/* add the extra CRLF if the person isn't in compact mode */
if (D_CHECK(t) && !PRF_FLAGGED(t->character, PRF_COMPACT))
strcat(i + 2, "\r\n");
/*
* now, send the output. If this is an 'interruption', use the prepended
* CRLF, otherwise send the straight output sans CRLF.
*/
if (!t->prompt_mode)
result = write_to_descriptor(t->descriptor, i);
else
result = write_to_descriptor(t->descriptor, i + 2);
if (t->character && IS_PC(t->character))
t->character->pc_specials->needs_update = FALSE;
/* handle snooping: prepend "% " and send to snooper */
if (t->snoop_by) {
send_to_q("% ", t->snoop_by);
send_to_q(t->output, t->snoop_by);
send_to_q("%%", t->snoop_by);
}
/*
* if we were using a large buffer, put the large buffer on the buffer pool
* and switch back to the small one
*/
if (t->large_outbuf) {
t->large_outbuf->next = bufpool;
bufpool = t->large_outbuf;
t->large_outbuf = NULL;
t->output = t->small_outbuf;
}
/* reset total bufspace back to that of a small buffer */
t->bufspace = SMALL_BUFSIZE - 1;
t->bufptr = 0;
*(t->output) = '\0';
return result;
}
int write_to_descriptor(int desc, char *txt)
{
int sofar, thisround, total;
total = strlen(txt);
sofar = 0;
do {
thisround = write(desc, txt + sofar, total - sofar);
if (thisround < 0) {
perror("Write to socket");
return(-1);
}
sofar += thisround;
} while (sofar < total);
return(0);
}
int process_input(dsdata *t)
{
int sofar, thisround, begin, squelch, i, k, flag, failed_subst = 0;
char tmp[MAX_INPUT_LENGTH+2], buffer[MAX_INPUT_LENGTH + 60];
char somebuf[MAX_STRING_LENGTH];
// if they is rerouted, dump the input off into cyberspacia -roa
if (t->rerouted)
{
read(t->descriptor, somebuf, MAX_STRING_LENGTH);
return 0;
}
sofar = 0;
flag = 0;
begin = strlen(t->buf);
/* Read in some stuff */
do
{
if ((thisround = read(t->descriptor, t->buf + begin + sofar,
MAX_STRING_LENGTH - (begin + sofar) - 1)) > 0)
sofar += thisround;
else
if (thisround < 0)
if (errno != EWOULDBLOCK)
{
perror("Read1 - ERROR");
return(-1);
}
else
break;
else
{
log("EOF encountered on socket read. (connection broken by peer)");
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 */
// this is normal case
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(*(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
if (*tmp == '^')
{
if (!(failed_subst = perform_subst(t, t->last_input, tmp)))
strcpy(t->last_input, tmp);
}
else
strcpy(t->last_input, tmp);
if (!failed_subst)
write_to_q(tmp, &t->input, 0);
if (t->snoop_by) {
send_to_q("% ", t->snoop_by);
send_to_q(tmp, t->snoop_by);
send_to_q("\n\r", t->snoop_by);
}
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);
}
/*
* perform substitution for the '^..^' csh-esque syntax
* orig is the orig string (i.e. the one being modified.
* subst contains the substition string, i.e. "^telm^tell"
*/
int perform_subst(dsdata *t, char *orig, char *subst)
{
char neww[MAX_INPUT_LENGTH + 5];
char *first, *second, *strpos;
/*
* first is the position of the beginning of the first string (the one
* to be replaced
*/
first = subst + 1;
/* now find the second '^' */
if (!(second = strchr(first, '^'))) {
send_to_q("Invalid substitution.\r\n", t);
return 1;
}
/* terminate "first" at the position of the '^' and make 'second' point
* to the beginning of the second string */
*(second++) = '\0';
/* now, see if the contents of the first string appear in the original */
if (!(strpos = strstr(orig, first))) {
send_to_q("Invalid substitution.\r\n", t);
return 1;
}
/* now, we construct the new string for output. */
/* first, everything in the original, up to the string to be replaced */
strncpy(neww, orig, (strpos - orig));
neww[(strpos - orig)] = '\0';
/* now, the replacement string */
strncat(neww, second, (MAX_INPUT_LENGTH - strlen(neww) - 1));
/* now, if there's anything left in the original after the string to
* replaced, copy that too. */
if (((strpos - orig) + strlen(first)) < strlen(orig))
strncat(neww, strpos + strlen(first), (MAX_INPUT_LENGTH - strlen(neww) - 1));
/* terminate the string in case of an overflow from strncat */
neww[MAX_INPUT_LENGTH - 1] = '\0';
strcpy(subst, neww);
return 0;
}
void cleanup_after_linkloss(dsdata *d)
{
extern void wax_room(rmdata *r);
extern void wax_object(obdata *o);
extern void wax_mobile(chdata *ch);
if (OBJ_EDITTED(d->character))
{
wax_object(OBJ_EDITTED(d->character));
FREENULL(OBJ_EDITTED(d->character));
sprintf(buf, "SYSUPD: Freeing editted object on %s.", GET_NAME(d->character));
mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE);
}
if (ROOM_EDITTED(d->character))
{
wax_room(ROOM_EDITTED(d->character));
// free it, since it's stuck on a character
FREENULL(ROOM_EDITTED(d->character));
sprintf(buf, "SYSUPD: Freeing editted room on %s.", GET_NAME(d->character));
mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE);
}
if (MOB_EDITTED(d->character))
{
wax_mobile(MOB_EDITTED(d->character));
FREENULL(MOB_EDITTED(d->character));
sprintf(buf, "SYSUPD: Freeing editted mobile on %s.", GET_NAME(d->character));
mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE);
}
if (PSTR1(d->character))
{
FREENULL(PSTR1(d->character));
sprintf(buf, "SYSUPD: Freeing PSTR1 on %s.", GET_NAME(d->character));
mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE);
}
if (PSTR2(d->character))
{
FREENULL(PSTR2(d->character));
sprintf(buf, "SYSUPD: Freeing PSTR2 on %s.", GET_NAME(d->character));
mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE);
}
FREENULL(d->character->tmp_skills);
}
void close_socket(dsdata *d)
{
dsdata *temp;
if (d->cdesc)
close_client_socket(d);
close(d->descriptor);
flush_queues(d);
/* Forget snooping */
if (d->snooping)
d->snooping->snoop_by = NULL;
if (d->snoop_by) {
send_to_q("Your victim is no longer among us.\r\n", d->snoop_by);
d->snoop_by->snooping = NULL;
}
if (d->room_snooping)
remove_room_snooper(&world[d->room_snooping], d);
if (d->character)
{
if (STATE(d) == CON_PLAYING)
{
// MUST check to see if this guy had spawned process off in some editor
// if so, kill its child
if (d->child_pid > 0)
{
sprintf(buf, "Closing child shell: %s.", GET_NAME(d->character));
mudlog(buf, BRF, MAX(LEV_IMM, GET_INVIS_LEV(d->character)),TRUE);
if ( kill(d->child_pid, SIGKILL) < 0 )
{
perror("shell kill:");
sprintf(buf, "%%1%%BSYSWAR%%0: Error in child kill, recommend immediate shutdown.");
mudlog(buf, BRF, LEV_IMM,TRUE);
}
d->child_pid = -1;
}
REMOVE_BIT(PLR_FLAGS(d->character), PLR_BUILDING | PLR_WRITING | PLR_MAILING);
save_char(d->character, NOWHERE);
act("$n has lost $s soul.", TRUE, d->character, 0, 0, TO_ROOM);
sprintf(buf, "Closing link to: %s.", GET_NAME(d->character));
mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE);
// unallocate any unneeded memory
cleanup_after_linkloss(d);
d->character->desc = NULL;
}
else
{
sprintf(buf, "Losing player: %s.", GET_NAME(d->character) ? GET_NAME(d->character) : "<null>");
mudlog(buf, CMP, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE);
free_char(d->character);
}
} else
mudlog("Losing descriptor without char.", CMP, LEV_IMM, TRUE);
/* JE 2/22/95 -- part of my unending quest to make switch stable */
if (d->original && d->original->desc)
d->original->desc = NULL;
REMOVE_FROM_LIST(d, descriptor_list, next);
FREENULL(d->showstr_head);
if (d->showstr_count)
FREENULL(d->showstr_vector);
free(d);
}
// updated to kick idle prelim clients 5/20/98 -jtrhone
void check_idle_passwords(void)
{
dsdata *d, *next_d;
cldesc *c, *next_c;
for (d = descriptor_list; d; d = next_d)
{
next_d = d->next;
if (DESCMENU_HANDLER(d) != (void *)getpasswd && DESCMENU_HANDLER(d) != (void *)getnewpasswd &&
DESCMENU_HANDLER(d) != (void *)confirmnewpasswd && DESCMENU_HANDLER(d) != (void *)getname &&
DESCMENU_HANDLER(d) != (void *)confirmname)
continue;
if (!d->idle_tics) {
d->idle_tics++;
continue;
} else {
sprintf(buf, "SYSUPD: Descriptor #%d timed out.",d->desc_num);
mudlog(buf, BUG, LEV_IMM, FALSE);
echo_on(d);
send_to_q("\r\nIdle-timed out... please reconnect.\r\n", d);
STATE(d) = CON_CLOSE;
}
}
for (c = prelim_list; c; c = next_c)
{
next_c = c->next;
if (++(c->timer) > 1)
close_prelim_socket(c);
}
}
/*
* I tried to universally convert Circle over to POSIX compliance, but
* alas, some systems are still straggling behind and don't have all the
* appropriate defines. In particular, NeXT 2.x defines O_NDELAY but not
* O_NONBLOCK. Krusty old NeXT machines! (Thanks to Michael Jones for
* this and various other NeXT fixes.)
*/
#ifndef O_NONBLOCK
#define O_NONBLOCK O_NDELAY
#endif
void nonblock(int s)
{
int flags;
flags = fcntl(s, F_GETFL, 0);
flags |= O_NONBLOCK;
if (fcntl(s, F_SETFL, flags) < 0) {
perror("Fatal error executing nonblock (comm.c)");
log("fatal error executing nonblock comm.c");
exit(1);
}
}
void block(int s)
{
int flags;
flags = fcntl(s, F_GETFL, 0);
REMOVE_BIT(flags,O_NONBLOCK);
if (fcntl(s, F_SETFL, flags) < 0) {
perror("Fatal error executing block (comm.c)");
log("fatal error executing block comm.c");
exit(1);
}
}
/* ******************************************************************
* signal-handling functions (formerly signals.c) *
****************************************************************** */
/*
* This is an implementation of signal() using sigaction() for portability.
* (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced
* Programming in the UNIX Environment_. We are specifying that all system
* calls _not_ be automatically restarted for uniformity, because BSD systems
* do not restart select(), even if SA_RESTART is used.
*
* Note that NeXT 2.x is not POSIX and does not have sigaction; therefore,
* I just define it to be the old signal. If your system doesn't have
* sigaction either, you can use the same fix.
*
* SunOS Release 4.0.2 (sun386) needs this too, according to Tim Aldric.
*/
#ifndef POSIX
#define my_signal(signo, func) signal(signo, func)
#else
sigfunc *my_signal(int signo, sigfunc * func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT; /* SunOS */
#endif
if (sigaction(signo, &act, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
#endif /* NeXT */
RETSIGTYPE checkpointing(int sig)
{
if (!tics)
{
log("SYSERR: CHECKPOINT shutdown: tics not updated");
/* abort(); */
sleep(20);
}
else
tics = 0;
setup_checkpoint();
}
RETSIGTYPE unrestrict_game(int sig)
{
extern struct ban_list_element *ban_list;
extern int num_invalid;
mudlog("Received SIGUSR2 - completely unrestricting game (emergent)", BRF, LEV_IMM, TRUE);
ban_list = NULL;
restrict = 0;
num_invalid = 0;
my_signal(SIGUSR2, unrestrict_game);
}
RETSIGTYPE hupsig(int sig)
{
// ignore all signals now... 3/6/98 -jtrhone
default_sigs();
log("Rcvd SIGHUP, SIGINT, or SIGTERM. Freeing up and Shutting down...");
circle_shutdown = TRUE;
}
/*
* set up the deadlock-protection so that the MUD aborts itself if it gets
* caught in an infinite loop for more than 8 minutes. Doesn't work with
* OS/2.
*/
void setup_checkpoint(void)
{
struct itimerval itime;
struct timeval interval;
interval.tv_sec = 480;
interval.tv_usec = 0;
itime.it_interval = interval;
itime.it_value = interval;
setitimer(ITIMER_VIRTUAL, &itime, NULL);
my_signal(SIGVTALRM, checkpointing);
}
void signal_setup(void)
{
extern RETSIGTYPE grim_reaper(int sig_received);
/*
* user signal 2: unrestrict game. Used for emergencies if you lock
* yourself out of the MUD somehow. (Duh...)
*/
my_signal(SIGUSR2, unrestrict_game);
setup_checkpoint();
/* just to be on the safe side: */
my_signal(SIGHUP, hupsig);
my_signal(SIGINT, hupsig);
my_signal(SIGTERM, hupsig);
my_signal(SIGPIPE, SIG_IGN);
my_signal(SIGALRM, SIG_IGN);
// RoA -jtrhone, lets make a grim_reaper to take care of errant children
my_signal(SIGCHLD, grim_reaper);
}
// for children that shouldn't exit any special way...
void default_sigs(void)
{
my_signal(SIGHUP, SIG_DFL);
my_signal(SIGINT, SIG_DFL);
my_signal(SIGTERM, SIG_DFL);
// however, have the children ignore these
my_signal(SIGCHLD, SIG_IGN);
my_signal(SIGVTALRM, SIG_IGN);
}
/* ****************************************************************
* Public routines for system-to-player-communication *
*******************************************************************/
void send_to_char(char *messg, chdata *ch)
{
char cstr[MAX_STRING_LENGTH];
if (ch->desc && messg)
{
color_decode(ch, messg, cstr);
send_to_q(cstr, ch->desc);
}
}
void send_to_room_snoopers(char *messg, int room, chdata *ch, int type)
{
dsdata *s, *next_s;
for (s = RSNOOPER(&world[room]); s; s=next_s)
{
next_s = s->next_rsnooper;
if (s->character)
{
if (type == TO_SNOOP ||
(type == TO_SNOOP_HIDE && ch && can_see(s->character, ch)))
{
send_to_char("%B%5**%0 ",s->character);
send_to_char(messg, s->character);
}
}
else
remove_room_snooper(&world[room], s);
}
}
void send_auction(char *messg)
{
dsdata *i;
char cstr[MAX_STRING_LENGTH];
if (messg)
for (i = descriptor_list; i; i = i->next)
{
if (D_CHECK(i) && SEND_OK(i->character) &&
!PRF_FLAGGED(i->character, PRF_NOAUCT) &&
!ROOM_FLAGGED2(i->character->in_room, SOUNDPROOF))
{
color_decode(i->character, messg, cstr);
send_to_q(cstr, i);
}
}
}
void send_to_arena(char *messg)
{
dsdata *i;
char cstr[MAX_STRING_LENGTH];
if (messg)
for (i = descriptor_list; i; i = i->next)
if (D_CHECK(i) && SEND_OK(i->character) &&
!PRF2_FLAGGED(i->character, PRF2_NOARENA))
{
color_decode(i->character, messg, cstr);
send_to_q(cstr, i);
}
}
void send_to_all(char *messg)
{
dsdata *i;
char cstr[MAX_STRING_LENGTH];
if (messg)
for (i = descriptor_list; i; i = i->next)
if (D_CHECK(i) && SEND_OK(i->character))
{
color_decode(i->character, messg, cstr);
send_to_q(cstr, i);
}
}
void send_to_outdoor(char *messg)
{
dsdata *i;
char cstr[MAX_STRING_LENGTH];
if (messg)
for (i = descriptor_list; i; i = i->next)
if (D_CHECK(i) && !INVALID_ROOM(i->character->in_room) &&
AWAKE(i->character) && SEND_OK(i->character) &&
OUTSIDE(i->character) && !ROOM_FLAGGED(i->character->in_room, NO_WEATHER))
{
color_decode(i->character, messg, cstr);
send_to_q(cstr, i);
}
}
void send_to_except(char *messg, chdata *ch)
{
dsdata *i;
if (messg)
for (i = descriptor_list; i; i = i->next)
if (ch->desc != i && !i->connected)
send_to_q(messg, i);
}
void send_to_room(char *messg, int room)
{
chdata *i;
for (i = world[room].people; i; i = i->next_in_room)
send_to_char(messg, i);
send_to_room_snoopers(messg, room, NULL, TO_SNOOP);
}
/* send to ppl in room who arent building/writing/mailing */
void send_to_room_not_busy(char *messg, int room)
{
chdata *i;
char mesg[MAX_STRING_LENGTH -10];
str_cpy(mesg, messg, MAX_STRING_LENGTH-10, "send_to_room_not_busy");
str_cat(mesg, "\n\r", MAX_STRING_LENGTH-10, "send_to_room_not_busy");
for (i = world[room].people; i; i = i->next_in_room)
if (AWAKE(i) && SEND_OK(i))
send_to_char(mesg, i);
send_to_room_snoopers(mesg, room, NULL, TO_SNOOP);
}
/* send to ppl in zone (outside) who arent building/writing/mailing */
void send_to_zone_outside(char *messg, int zone)
{
int r;
for (r = 0; r < top_of_world; r++)
if (world[r].zone == zone && WEATHER_ROOM(r))
send_to_room_not_busy(messg, r);
}
void send_to_room_except(char *messg, int room, chdata *ch)
{
chdata *i;
for (i = world[room].people; i; i = i->next_in_room)
if (i != ch)
send_to_char(messg, i);
}
void send_to_room_except_two(char *messg, int room, chdata *ch1, chdata *ch2)
{
chdata *i;
for (i = world[room].people; i; i = i->next_in_room)
if (i != ch1 && i != ch2)
send_to_char(messg, i);
}
char *ACTNULL = "<NULL>";
#define CHECK_NULL(pointer, expression) \
if ((pointer) == NULL) i = ACTNULL; else i = (expression);
/* higher-level communication: the act() function */
void perform_act(char *orig, chdata *ch, obdata *obj, void *vict_obj, chdata *to, int type)
{
register char *i = NULL, *buf;
static char lbuf[MAX_STRING_LENGTH];
char cstr[MAX_STRING_LENGTH];
char *debug_string;
buf = lbuf;
debug_string = orig; // so we can log violating strings from beginning -roa
for (;;) {
if (*orig == '$') {
switch (*(++orig)) {
case 'n':
i = PERS(ch, to);
break;
case 'N':
CHECK_NULL(vict_obj, PERS((chdata *) vict_obj, to));
break;
case 'm':
i = HMHR(ch);
break;
case 'M':
CHECK_NULL(vict_obj, HMHR((chdata *) vict_obj));
break;
case 's':
i = HSHR(ch);
break;
case 'S':
CHECK_NULL(vict_obj, HSHR((chdata *) vict_obj));
break;
case 'e':
i = HSSH(ch);
break;
case 'E':
CHECK_NULL(vict_obj, HSSH((chdata *) vict_obj));
break;
case 'o':
CHECK_NULL(obj, OBJN(obj, to));
break;
case 'O':
CHECK_NULL(vict_obj, OBJN((obdata *) vict_obj, to));
break;
case 'p':
CHECK_NULL(obj, OBJS(obj, to));
break;
case 'P':
CHECK_NULL(vict_obj, OBJS((obdata *) vict_obj, to));
break;
case 'a':
CHECK_NULL(obj, SANA(obj));
break;
case 'A':
CHECK_NULL(vict_obj, SANA((obdata *) vict_obj));
break;
case 'T':
CHECK_NULL(vict_obj, (char *) vict_obj);
break;
case 'F':
CHECK_NULL(vict_obj, fname((char *) vict_obj));
break;
case '$':
i = "$";
break;
default:
mudlog("SYSERR: Illegal $-code to act():", BRF, LEV_IMM, FALSE);
sprintf(buf1, "SYSERR: (%s)", debug_string);
mudlog(buf1, BRF, LEV_IMM, FALSE);
return;
break;
}
while ((*buf = *(i++)))
buf++;
orig++;
} else if (!(*(buf++) = *(orig++)))
break;
}
*(--buf) = '\r';
*(++buf) = '\n';
*(++buf) = '\0';
CCAP(lbuf);
// roa snoop/zone/world additions -jtrhone
switch (type) {
case TO_SNOOP:
case TO_SNOOP_HIDE:
send_to_room_snoopers(lbuf, to->in_room, to, type);
return;
default:
break;
}
if (!INCHAT(to))
{
color_decode(to, lbuf, cstr);
send_to_q(cstr, to->desc);
}
}
#define SENDOK(ch) ((ch)->desc && (AWAKE(ch) || sleep) && SEND_OK((ch)))
void act(char *str, int hide_invisible, chdata *ch, obdata *obj, void *vict_obj, int type)
{
chdata *to;
static int sleep;
if (!str || !*str)
return;
/*
* Warning: the following TO_SLEEP code is a hack.
*
* I wanted to be able to tell act to deliver a message regardless of sleep
* without adding an additional argument. TO_SLEEP is 128 (a single bit
* high up). It's ONLY legal to combine TO_SLEEP with one other TO_x
* command. It's not legal to combine TO_x's with each other otherwise.
*/
/* check if TO_SLEEP is there, and remove it if it is. */
if ((sleep = (type & TO_SLEEP)))
type &= ~TO_SLEEP;
if (type == TO_CHAR) {
if (ch && SENDOK(ch))
perform_act(str, ch, obj, vict_obj, ch, type);
return;
}
if (type == TO_VICT) {
if ((to = (chdata *) vict_obj) && SENDOK(to))
perform_act(str, ch, obj, vict_obj, to, type);
return;
}
/* ASSUMPTION: at this point we know type must be TO_NOTVICT or TO_ROOM */
if (ch && !IN_NOWHERE(ch))
to = world[ch->in_room].people;
else if (obj && !IN_NOWHERE(obj))
to = world[obj->in_room].people;
else {
log("SYSERR: no valid target to act()!");
return;
}
if (type == TO_ROOM) // send to room snoopers, one time
{
if (hide_invisible)
perform_act(str, ch, obj, vict_obj, ch, TO_SNOOP_HIDE);
else
perform_act(str, ch, obj, vict_obj, ch, TO_SNOOP);
}
switch(type) {
case TO_ROOM:
case TO_NOTVICT:
for (; to; to = to->next_in_room)
if (SENDOK(to) && !(hide_invisible && ch && !can_see(to, ch)) &&
(to != ch) && (type == TO_ROOM || (to != vict_obj)))
perform_act(str, ch, obj, vict_obj, to, type);
return;
case TO_ZONE_MORTLOG:
for (to=character_list; to && ch; to=to->next)
if (SENDOK(to) && !(hide_invisible && ch && !can_see(to, ch)) &&
(to != ch) && PRF_FLAGGED(to, PRF_MORTLOG) &&
world[to->in_room].zone == world[ch->in_room].zone)
perform_act(str, ch, obj, vict_obj, to, type);
return;
case TO_WORLD_MORTLOG:
for (to=character_list; to && ch; to=to->next)
if (SENDOK(to) && !(hide_invisible && ch && !can_see(to, ch)) &&
(to != ch) && PRF_FLAGGED(to, PRF_MORTLOG))
perform_act(str, ch, obj, vict_obj, to, type);
return;
}
}