/*
* $Id: comm.c,v 1.176 1999/04/17 10:12:47 fjoe Exp $
*/
/***************************************************************************
* ANATOLIA 2.1 is copyright 1996-1997 Serdar BULUT, Ibrahim CANPUNAR *
* ANATOLIA has been brought to you by ANATOLIA consortium *
* Serdar BULUT {Chronos} bulut@rorqual.cc.metu.edu.tr *
* Ibrahim Canpunar {Asena} canpunar@rorqual.cc.metu.edu.tr *
* Murat BICER {KIO} mbicer@rorqual.cc.metu.edu.tr *
* D.Baris ACAR {Powerman} dbacar@rorqual.cc.metu.edu.tr *
* By using this code, you have agreed to follow the terms of the *
* ANATOLIA license, in the file Anatolia/anatolia.licence *
***************************************************************************/
/***************************************************************************
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* *
* Merc Diku vMud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* In order to use any part of this Merc Diku Mud, you must comply with *
* both the original Diku license in 'license.doc' as well the Merc *
* license in 'license.txt'. In particular, you may not remove either of *
* these copyright notices. *
* *
* Thanks to abaddon for proof-reading our comm.c and pointing out bugs. *
* Any remaining bugs are, of course, our work, not his. :) *
* *
* Much time and thought has gone into this software and you are *
* benefitting. We hope that you share your changes too. What goes *
* around, comes around. *
***************************************************************************/
/***************************************************************************
* ROM 2.4 is copyright 1993-1995 Russ Taylor *
* ROM has been brought to you by the ROM consortium *
* Russ Taylor (rtaylor@pacinfo.com) *
* Gabrielle Taylor (gtaylor@pacinfo.com) *
* Brian Moore (rom@rom.efn.org) *
* By using this code, you have agreed to follow the terms of the *
* ROM license, in the file Rom24/doc/rom.license *
***************************************************************************/
/*
* This file contains all of the OS-dependent stuff:
* startup, BSD sockets for tcp/ip, i/o, timing.
*
* The data flow for input is:
* Game_loop ---> Read_from_descriptor ---> Read
* Game_loop ---> Read_from_buffer
*
* The data flow for output is:
* Game_loop ---> Process_Output ---> Write_to_descriptor -> Write
*
* The OS-dependent functions are Read_from_descriptor and Write_to_descriptor.
* -- Furey 26 Jan 1993
*/
#include <sys/types.h>
#if !defined(WIN32)
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/telnet.h>
# include <arpa/inet.h>
# include <unistd.h>
# include <netdb.h>
# include <sys/wait.h>
#else
# include <winsock.h>
# include <sys/timeb.h>
#endif
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(SUNOS) || defined(SVR4) || defined(LINUX)
# include <crypt.h>
#endif
#include "merc.h"
#include "interp.h"
#include "quest.h"
#include "update.h"
#include "ban.h"
#include "charset.h"
#include "resolver.h"
#include "olc/olc.h"
#include "comm_info.h"
#include "comm_colors.h"
#include "db/lang.h"
DESCRIPTOR_DATA * new_descriptor (void);
void free_descriptor (DESCRIPTOR_DATA *d);
bool class_ok(CHAR_DATA *ch , int class);
struct codepage {
char* name;
unsigned char* from;
unsigned char* to;
};
struct codepage codepages[] = {
{ "koi8-r", koi8_koi8, koi8_koi8 },
{ "alt (cp866)", alt_koi8, koi8_alt },
{ "win (cp1251)", win_koi8, koi8_win },
{ "iso (ISO-8859-5)", iso_koi8, koi8_iso },
{ "mac", mac_koi8, koi8_mac },
{ "translit", koi8_koi8, koi8_vola },
};
#define NCODEPAGES (sizeof(codepages) / sizeof(struct codepage))
/*
* Socket and TCP/IP stuff.
*/
#if defined (WIN32)
#include <winsock.h>
void gettimeofday args( ( struct timeval *tp, void *tzp ) );
/* Definitions for the TELNET protocol. Copied from telnet.h */
#define IAC 255 /* interpret as command: */
#define DONT 254 /* you are not to use option */
#define DO 253 /* please, you use option */
#define WONT 252 /* I won't use option */
#define WILL 251 /* I will use option */
#define SB 250 /* interpret as subnegotiation */
#define GA 249 /* you may reverse the line */
#define EL 248 /* erase the current line */
#define EC 247 /* erase the current character */
#define AYT 246 /* are you there */
#define AO 245 /* abort output--but let prog finish */
#define IP 244 /* interrupt process--permanently */
#define BREAK 243 /* break */
#define DM 242 /* data mark--for connect. cleaning */
#define NOP 241 /* nop */
#define SE 240 /* end sub negotiation */
#define EOR 239 /* end of record (transparent mode) */
#define SYNCH 242 /* for telfunc calls */
#define TELOPT_ECHO 1 /* echo */
#endif
char echo_off_str [] = { IAC, WILL, TELOPT_ECHO, '\0' };
char echo_on_str [] = { IAC, WONT, TELOPT_ECHO, '\0' };
char go_ahead_str [] = { IAC, GA, '\0' };
/*
* Global variables.
*/
DESCRIPTOR_DATA * descriptor_list; /* All open descriptors */
DESCRIPTOR_DATA * d_next; /* Next descriptor in loop */
bool merc_down; /* Shutdown */
bool wizlock; /* Game is wizlocked */
bool newlock; /* Game is newlocked */
char str_boot_time[26];
time_t current_time; /* time of this pulse */
int iNumPlayers = 0; /* The number of players on */
extern int max_on;
int init_socket (int port);
void process_who (int port);
void init_descriptor (int control);
void close_descriptor (DESCRIPTOR_DATA *d);
bool read_from_descriptor (DESCRIPTOR_DATA *d);
bool write_to_descriptor (int desc, char *txt, uint length);
void game_loop_unix (void);
#if !defined(WIN32)
void resolv_done (void);
#endif
/*
* Other local functions (OS-independent).
*/
bool check_reconnect (DESCRIPTOR_DATA *d, const char *name,
bool fConn);
bool check_playing (DESCRIPTOR_DATA *d, const char *name);
int main (int argc, char **argv);
void nanny (DESCRIPTOR_DATA *d, const char *argument);
bool process_output (DESCRIPTOR_DATA *d, bool fPrompt);
void read_from_buffer (DESCRIPTOR_DATA *d);
void stop_idling (CHAR_DATA *ch);
void bust_a_prompt (CHAR_DATA *ch);
void log_area_popularity (void);
varr control_sockets = { sizeof(int), 2 };
varr info_sockets = { sizeof(int), 2 };
varr info_trusted = { sizeof(struct in_addr), 2 };
static void usage(const char *name)
{
fprintf(stderr, "Usage: %s [-p port...] [-i port...]\n"
"Where:\n"
"\t-p -- listen port\n"
"\t-i -- info service port\n",
get_filename(name));
exit(1);
}
#define GETINT(v, i) (*(int*) VARR_GET(v, i))
static void open_sockets(varr *v, const char *logm)
{
int i, j;
for (i = 0, j = 0; i < v->nused; i++) {
int port = GETINT(v, i);
int sock;
if ((sock = init_socket(port)) < 0)
continue;
log_printf(logm, port);
GETINT(v, j++) = sock;
}
v->nused = j;
}
void close_sockets(varr *v)
{
int i;
for (i = 0; i < v->nused; i++) {
int fd = GETINT(v, i);
#if defined (WIN32)
closesocket(fd);
#else
close(fd);
#endif
}
}
int main(int argc, char **argv)
{
struct timeval now_time;
int ch;
int check_info;
#if defined WIN32
WORD wVersionRequested = MAKEWORD(1, 1);
WSADATA wsaData;
int err;
#endif
/*
* Memory debugging if needed.
*/
#if defined(MALLOC_DEBUG)
malloc_debug(2);
#endif
setlocale(LC_ALL, "");
/*
* Init time.
*/
gettimeofday(&now_time, NULL);
current_time = (time_t) now_time.tv_sec;
strnzcpy(str_boot_time, sizeof(str_boot_time), strtime(current_time));
/*
* Run the game.
*/
#if defined (WIN32)
srand((unsigned) time(NULL));
err = WSAStartup(wVersionRequested, &wsaData);
if (err) {
log_printf("winsock.dll: %s", strerror(errno));
exit(1);
}
#else
resolver_init();
#endif
boot_db_system();
if (argc > 1) {
/*
* command line parameters override configuration settings
*/
control_sockets.nused = 0;
info_sockets.nused = 0;
opterr = 0;
while ((ch = getopt(argc, argv, "p:i:")) != -1) {
int *p;
switch (ch) {
case 'p':
if (!is_number(optarg))
usage(argv[0]);
p = varr_enew(&control_sockets);
*p = atoi(optarg);
break;
case 'i':
if (!is_number(optarg))
usage(argv[0]);
p = varr_enew(&info_sockets);
*p = atoi(optarg);
break;
default:
usage(argv[0]);
}
}
argc -= optind;
argv += optind;
}
if (!control_sockets.nused) {
log_printf("no control sockets defined");
exit(1);
}
check_info = (!!info_sockets.nused);
boot_db();
open_sockets(&control_sockets, "ready to rock on port %d");
open_sockets(&info_sockets, "info service started on port %d");
if (!control_sockets.nused) {
log_printf("no control sockets could be opened.");
exit(1);
}
if (check_info && !info_sockets.nused) {
log_printf("no info service sockets could be opened.");
exit(1);
}
game_loop_unix();
close_sockets(&control_sockets);
close_sockets(&info_sockets);
#if defined (WIN32)
WSACleanup();
#else
resolver_done();
#endif
log_area_popularity();
/*
* That's all, folks.
*/
log("Normal termination of game.");
return 0;
}
/* stuff for recycling descriptors */
DESCRIPTOR_DATA *descriptor_free;
DESCRIPTOR_DATA *new_descriptor(void)
{
DESCRIPTOR_DATA *d;
if (descriptor_free == NULL)
d = malloc(sizeof(*d));
else {
d = descriptor_free;
descriptor_free = descriptor_free->next;
}
memset(d, 0, sizeof(*d));
return d;
}
void free_descriptor(DESCRIPTOR_DATA *d)
{
if (!d)
return;
free_string(d->host);
free(d->outbuf);
d->next = descriptor_free;
descriptor_free = d;
}
int init_socket(int port)
{
static struct sockaddr_in sa_zero;
struct sockaddr_in sa;
struct linger ld;
int x = 1;
int fd;
#if defined (WIN32)
if ((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
#else
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
#endif
log_printf("init_socket(%d): socket: %s",
port, strerror(errno));
return -1;
}
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
(char *) &x, sizeof(x)) < 0) {
log_printf("init_socket(%d): setsockopt: SO_REUSEADDR: %s",
port, strerror(errno));
#if defined (WIN32)
closesocket(fd);
#else
close(fd);
#endif
return -1;
}
ld.l_onoff = 0;
if (setsockopt(fd, SOL_SOCKET, SO_LINGER,
(char *) &ld, sizeof(ld)) < 0) {
log_printf("init_socket(%d): setsockopt: SO_LINGER: %s",
port, strerror(errno));
#if defined (WIN32)
closesocket(fd);
#else
close(fd);
#endif
return -1;
}
sa = sa_zero;
#if !defined (WIN32)
sa.sin_family = AF_INET;
#else
sa.sin_family = PF_INET;
#endif
sa.sin_port = htons(port);
if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
log_printf("init_socket(%d): bind: %s", port, strerror(errno));
#if defined (WIN32)
closesocket(fd);
#else
close(fd);
#endif
return -1;
}
if (listen(fd, 3) < 0) {
log_printf("init_socket(%d): listen: %s",
port, strerror(errno));
#if defined (WIN32)
closesocket(fd);
#else
close(fd);
#endif
return -1;
}
return fd;
}
static void add_fds(varr *v, fd_set *in_set, int *maxdesc)
{
int i;
for (i = 0; i < v->nused; i++) {
int fd = GETINT(v, i);
FD_SET(fd, in_set);
if (*maxdesc < fd) *maxdesc = fd;
}
}
static void check_fds(varr *v, fd_set *in_set, void (*new_conn_cb)(int))
{
int i;
for (i = 0; i < v->nused; i++) {
int fd = GETINT(v, i);
if (FD_ISSET(fd, in_set))
new_conn_cb(fd);
}
}
void game_loop_unix(void)
{
static struct timeval null_time;
struct timeval last_time;
gettimeofday(&last_time, NULL);
current_time = (time_t) last_time.tv_sec;
/* Main loop */
while (!merc_down) {
fd_set in_set;
fd_set out_set;
fd_set exc_set;
DESCRIPTOR_DATA *d;
INFO_DESC *id;
INFO_DESC *id_next;
int maxdesc;
#if defined(MALLOC_DEBUG)
if (malloc_verify() != 1)
abort();
#endif
/*
* Poll all active descriptors.
*/
FD_ZERO(&in_set );
FD_ZERO(&out_set);
FD_ZERO(&exc_set);
maxdesc = 0;
add_fds(&control_sockets, &in_set, &maxdesc);
add_fds(&info_sockets, &in_set, &maxdesc);
#if !defined (WIN32)
FD_SET(fileno(rfin), &in_set);
maxdesc = UMAX(maxdesc, fileno(rfin));
#endif
for (d = descriptor_list; d; d = d->next) {
maxdesc = UMAX(maxdesc, d->descriptor);
FD_SET(d->descriptor, &in_set );
FD_SET(d->descriptor, &out_set);
FD_SET(d->descriptor, &exc_set);
}
for (id = id_list; id; id = id->next) {
maxdesc = UMAX(maxdesc, id->fd);
FD_SET(id->fd, &in_set);
}
if (select(maxdesc+1,
&in_set, &out_set, &exc_set, &null_time) < 0) {
log_printf("game_loop: select: %s", strerror(errno));
exit(1);
}
#if !defined (WIN32)
if (FD_ISSET(fileno(rfin), &in_set))
resolv_done();
#endif
check_fds(&control_sockets, &in_set, init_descriptor);
check_fds(&info_sockets, &in_set, info_newconn);
for (id = id_list; id; id = id_next) {
id_next = id->next;
if (FD_ISSET(id->fd, &in_set))
info_process_cmd(id);
}
/*
* Kick out the freaky folks.
*/
for (d = descriptor_list; d; d = d_next) {
d_next = d->next;
if (FD_ISSET(d->descriptor, &exc_set)) {
FD_CLR(d->descriptor, &in_set );
FD_CLR(d->descriptor, &out_set);
if (d->character && d->character->level > 1)
save_char_obj(d->character, FALSE);
d->outtop = 0;
close_descriptor(d);
}
}
/*
* Process input.
*/
for (d = descriptor_list; d != NULL; d = d_next) {
d_next = d->next;
d->fcommand = FALSE;
if (FD_ISSET(d->descriptor, &in_set)) {
if (d->character != NULL)
d->character->timer = 0;
if (!read_from_descriptor(d)) {
FD_CLR(d->descriptor, &out_set);
if (d->character != NULL
&& d->character->level > 1)
save_char_obj(d->character,
FALSE);
d->outtop = 0;
close_descriptor(d);
continue;
}
}
if (d->character != NULL && d->character->daze > 0)
--d->character->daze;
if (d->character != NULL && d->character->wait > 0) {
--d->character->wait;
continue;
}
read_from_buffer(d);
if (d->incomm[0] != '\0') {
d->fcommand = TRUE;
stop_idling(d->character);
if (d->showstr_point)
show_string(d, d->incomm);
else if (d->pString)
string_add(d->character, d->incomm);
else if (d->connected == CON_PLAYING) {
if (!run_olc_editor(d))
substitute_alias(d, d->incomm);
}
else
nanny(d, d->incomm);
if (d->connected != CON_RESOLV)
d->incomm[0] = '\0';
}
}
/*
* Autonomous game motion.
*/
update_handler();
/*
* Output.
*/
for (d = descriptor_list; d != NULL; d = d_next) {
d_next = d->next;
if ((d->fcommand || d->outtop > 0)
&& FD_ISSET(d->descriptor, &out_set)) {
if (!process_output(d, TRUE)) {
if (d->character != NULL
&& d->character->level > 1)
save_char_obj(d->character, FALSE);
d->outtop = 0;
close_descriptor(d);
}
}
}
/*
* Synchronize to a clock.
* Sleep(last_time + 1/PULSE_PER_SCD - now).
* Careful here of signed versus unsigned arithmetic.
*/
#if !defined (WIN32)
{
struct timeval now_time;
long secDelta;
long usecDelta;
gettimeofday(&now_time, NULL);
usecDelta = ((int) last_time.tv_usec) - ((int) now_time.tv_usec)
+ 1000000 / PULSE_PER_SCD;
secDelta = ((int) last_time.tv_sec) - ((int) now_time.tv_sec);
while (usecDelta < 0) {
usecDelta += 1000000;
secDelta -= 1;
}
while (usecDelta >= 1000000) {
usecDelta -= 1000000;
secDelta += 1;
}
if (secDelta > 0 || (secDelta == 0 && usecDelta > 0)) {
struct timeval stall_time;
stall_time.tv_usec = usecDelta;
stall_time.tv_sec = secDelta;
if (select(0, NULL, NULL, NULL, &stall_time) < 0) {
log_printf("game_loop: select: stall: %s", strerror(errno));
exit(1);
}
}
}
#else
{
int times_up;
int nappy_time;
struct _timeb start_time;
struct _timeb end_time;
_ftime( &start_time );
times_up = 0;
while( times_up == 0 )
{
_ftime( &end_time );
if ( ( nappy_time = (int) ( 1000 * (double) ( ( end_time.time - start_time.time ) +
( (double) ( end_time.millitm - start_time.millitm ) /
1000.0 ) ) ) ) >= (double)( 1000 / PULSE_PER_SCD ) )
times_up = 1;
else
{
Sleep( (int) ( (double) ( 1000 / PULSE_PER_SECOND ) -
(double) nappy_time ) );
times_up = 1;
}
}
}
#endif
gettimeofday(&last_time, NULL);
current_time = (time_t) last_time.tv_sec;
}
}
static void cp_print(DESCRIPTOR_DATA* d)
{
char buf[MAX_STRING_LENGTH];
int i;
write_to_buffer(d, "\n\r", 0);
for (i = 0; i < NCODEPAGES; i++) {
snprintf(buf, sizeof(buf), "%s%d. %s",
i ? " " : "", i+1, codepages[i].name);
write_to_buffer(d, buf, 0);
}
write_to_buffer(d, "\n\rSelect your codepage (non-russian players should choose translit): ", 0);
}
#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif
void init_descriptor(int control)
{
DESCRIPTOR_DATA *dnew;
struct sockaddr_in sock;
int desc;
int size;
HELP_DATA *greeting;
size = sizeof(sock);
getsockname(control, (struct sockaddr *) &sock, &size);
if ((desc = accept(control, (struct sockaddr *) &sock, &size)) < 0) {
log_printf("init_descriptor: accept: %s", strerror(errno));
return;
}
#if !defined (WIN32)
if (fcntl(desc, F_SETFL, FNDELAY) < 0) {
log_printf("init_descriptor: fcntl: FNDELAY: %s",
strerror(errno));
return;
}
#endif
/*
* Cons a new descriptor.
*/
dnew = new_descriptor();
dnew->descriptor = desc;
dnew->connected = CON_GET_CODEPAGE;
dnew->showstr_head = NULL;
dnew->showstr_point = NULL;
dnew->pString = NULL;
dnew->olced = NULL;
dnew->pEdit = NULL;
dnew->pEdit2 = NULL;
dnew->outsize = 2000;
dnew->outbuf = malloc(dnew->outsize);
dnew->wait_for_se = 0;
dnew->codepage = codepages;
dnew->host = NULL;
size = sizeof(sock);
if (getpeername(desc, (struct sockaddr *) &sock, &size) < 0) {
log_printf("init_descriptor: getpeername: %s",
strerror(errno));
return;
}
#if defined (WIN32)
else {
/* Copying from ROM 2.4b6 */
int addr;
struct hostent *from;
addr = ntohl(sock.sin_addr.s_addr);
from = gethostbyaddr((char *) &sock.sin_addr,
sizeof(sock.sin_addr), AF_INET);
dnew->host = str_dup(from ? from->h_name : "unknown");
}
#endif
log_printf("sock.sinaddr: %s", inet_ntoa(sock.sin_addr));
dnew->next = descriptor_list;
descriptor_list = dnew;
/*
* Send the greeting.
*/
if ((greeting = help_lookup(1, "GREETING"))) {
char buf[MAX_STRING_LENGTH];
parse_colors(mlstr_mval(greeting->text), buf, sizeof(buf),
FORMAT_DUMB);
write_to_buffer(dnew, buf + (buf[0] == '.'), 0);
}
cp_print(dnew);
}
void close_descriptor(DESCRIPTOR_DATA *dclose)
{
CHAR_DATA *ch;
DESCRIPTOR_DATA *d;
if (dclose->outtop > 0)
process_output(dclose, FALSE);
if (dclose->snoop_by != NULL)
write_to_buffer(dclose->snoop_by,
"Your victim has left the game.\n\r", 0);
for (d = descriptor_list; d != NULL; d = d->next)
if (d->snoop_by == dclose)
d->snoop_by = NULL;
if ((ch = dclose->character) != NULL) {
log_printf("Closing link to %s.", ch->name);
if (dclose->connected == CON_PLAYING) {
act("$n has lost $s link.", ch, NULL, NULL, TO_ROOM);
wiznet("Net death has claimed $N.", ch, NULL,
WIZ_LINKS, 0, 0);
ch->desc = NULL;
}
else
free_char(dclose->character);
}
if (d_next == dclose)
d_next = d_next->next;
if (dclose == descriptor_list)
descriptor_list = descriptor_list->next;
else {
DESCRIPTOR_DATA *d;
for (d = descriptor_list; d && d->next != dclose; d = d->next)
;
if (d != NULL)
d->next = dclose->next;
else
bug("Close_socket: dclose not found.", 0);
}
#if !defined( WIN32 )
close(dclose->descriptor);
#else
closesocket(dclose->descriptor);
#endif
free_descriptor(dclose);
}
bool read_from_descriptor(DESCRIPTOR_DATA *d)
{
int iOld;
int iStart;
unsigned char *p, *q;
/*
* Hold horses if pending command already
*/
if (d->incomm[0] != '\0')
return TRUE;
/* Check for overflow. */
iOld = iStart = strlen(d->inbuf);
if (iStart >= sizeof(d->inbuf) - 10) {
log_printf("%s input overflow!", d->host);
write_to_descriptor(d->descriptor,
"\n\r*** PUT A LID ON IT!!! ***\n\r", 0);
return FALSE;
}
for (; ;) {
int nRead;
#if !defined (WIN32)
nRead = read( d->descriptor, d->inbuf + iStart,
sizeof( d->inbuf ) - 10 - iStart );
#else
nRead = recv( d->descriptor, d->inbuf + iStart,
sizeof( d->inbuf ) - 10 - iStart, 0 );
#endif
if (nRead > 0) {
iStart += nRead;
if (d->inbuf[iStart-1] == '\n'
|| d->inbuf[iStart-1] == '\r')
break;
}
else if (nRead == 0) {
log("EOF encountered on read.");
return FALSE;
break;
}
#if !defined (WIN32)
else if (errno == EWOULDBLOCK)
break;
#else
else if ( WSAGetLastError() == WSAEWOULDBLOCK)
break;
#endif
else {
log_printf("read_from_descriptor: %s", strerror(errno));
return FALSE;
}
}
d->inbuf[iStart] = '\0';
if (iOld == iStart)
return TRUE;
for (p = d->inbuf+iOld; *p;) {
unsigned char *r;
if (*p != IAC
|| (d->connected == CON_PLAYING &&
d->character &&
IS_SET(d->character->comm, COMM_NOTELNET))) {
p++;
continue;
}
if (d->wait_for_se)
goto wse;
switch (p[1]) {
case DONT:
case DO:
case WONT:
case WILL:
q = p+3;
break;
wse:
case SB:
q = strchr(p, SE);
if (q == NULL) {
q = strchr(p, '\0');
d->wait_for_se = 1;
}
else {
q++;
d->wait_for_se = 0;
}
break;
case IAC:
memmove(p, p+1, strlen(p));
p++;
continue;
/* NOTREACHED */
default:
q = p+2;
break;
}
if ((r = strchr(p, '\0')) < q)
q = r;
memmove(p, q, strlen(q)+1);
}
return TRUE;
}
/*
* Transfer one line from input buffer to input line.
*/
void read_from_buffer(DESCRIPTOR_DATA *d)
{
int i, j, k;
/*
* Hold horses if pending command already.
*/
if (d->incomm[0] != '\0')
return;
/*
* Look for at least one new line.
*/
for (i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++)
if (d->inbuf[i] == '\0')
return;
/*
* Canonical input processing.
*/
for (i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++) {
if (k >= MAX_INPUT_LENGTH - 2) {
write_to_descriptor(d->descriptor,
"Line too long.\n\r", 0);
/* skip the rest of the line */
for (; d->inbuf[i] != '\0'; i++)
if (d->inbuf[i] == '\n' || d->inbuf[i] == '\r')
break;
d->inbuf[i] = '\n';
d->inbuf[i+1] = '\0';
break;
}
if (d->inbuf[i] == '\b' && k > 0)
--k;
else if ((unsigned)d->inbuf[i] >= ' ')
d->incomm[k++] =
d->codepage->from[(unsigned char) d->inbuf[i]];
}
/*
* Finish off the line.
*/
if (k == 0)
d->incomm[k++] = ' ';
d->incomm[k] = '\0';
/*
* Deal with bozos with #repeat 1000 ...
*/
if (k > 1 || d->incomm[0] == '!') {
if (d->incomm[0] != '!' && strcmp(d->incomm, d->inlast))
d->repeat = 0;
else {
CHAR_DATA *ch = d->original ? d->original :
d->character;
if (ch && ++d->repeat >= 100) {
char buf[MAX_STRING_LENGTH];
log_printf("%s input spamming!", d->host);
snprintf(buf, sizeof(buf),
"Inlast:[%s] Incomm:[%s]!",
d->inlast, d->incomm);
wiznet("SPAM SPAM SPAM $N spamming, and OUT!",
ch, NULL, WIZ_SPAM, 0, ch->level);
wiznet("[$N]'s $t!",
ch, buf, WIZ_SPAM, 0, ch->level);
write_to_descriptor(d->descriptor, "\n\r*** PUT A LID ON IT!!! ***\n\r", 0);
d->repeat = 0;
if (d->showstr_point) {
if (d->showstr_head) {
free_string(d->showstr_head);
d->showstr_head = NULL;
}
d->showstr_point = NULL;
}
if (d->pString) {
free_string(*d->pString);
*d->pString = d->backup;
d->pString = NULL;
}
strnzcpy(d->incomm, sizeof(d->incomm), "quit");
}
}
}
/*
* Do '!' substitution.
*/
if (d->incomm[0] == '!')
strnzcpy(d->incomm, sizeof(d->incomm), d->inlast);
else
strnzcpy(d->inlast, sizeof(d->inlast), d->incomm);
/*
* Shift the input buffer.
*/
while (d->inbuf[i] == '\n' || d->inbuf[i] == '\r')
i++;
for (j = 0; (d->inbuf[j] = d->inbuf[i+j]) != '\0'; j++)
;
}
/*
* Low level output function.
*/
void battle_prompt(CHAR_DATA *ch, CHAR_DATA *victim)
{
int percent;
char* msg;
if (victim->max_hit > 0)
percent = victim->hit * 100 / victim->max_hit;
else
percent = -1;
if (percent >= 100)
msg = "{Cis in perfect health{x.";
else if (percent >= 90)
msg = "{bhas a few scratches{x.";
else if (percent >= 75)
msg = "{Bhas some small but disgusting cuts{x.";
else if (percent >= 50)
msg = "{Gis covered with bleeding wounds{x.";
else if (percent >= 30)
msg = "{Yis gushing blood{x.";
else if (percent >= 15)
msg = "{Mis writhing in agony{x.";
else if (percent >= 0)
msg = "{Ris convulsing on the ground{x.";
else
msg = "{Ris nearly dead{x.";
act_puts("$N $t", ch, msg, victim, TO_CHAR | ACT_TRANS, POS_DEAD);
}
/*
* Some specials added by KIO
*/
bool process_output(DESCRIPTOR_DATA *d, bool fPrompt)
{
extern bool merc_down;
bool ga = FALSE;
/*
* Bust a prompt.
*/
if (!merc_down) {
CHAR_DATA *ch = d->character;
if (d->showstr_point) {
write_to_buffer(d, "[Hit Return to continue]\n\r", 0);
ga = TRUE;
}
else if (fPrompt && d->connected == CON_PLAYING) {
if (d->pString) {
write_to_buffer(d, " > ", 0);
ga = TRUE;
}
else if (ch) {
CHAR_DATA *victim;
/* battle prompt */
if ((victim = ch->fighting) != NULL
&& can_see(ch,victim))
battle_prompt(ch, victim);
if (!IS_SET(ch->comm, COMM_COMPACT))
write_to_buffer(d, "\n\r", 2);
if (IS_SET(ch->comm, COMM_PROMPT))
bust_a_prompt(d->character);
ga = TRUE;
}
}
if (ch && ga && !IS_SET(ch->comm, COMM_TELNET_GA))
ga = FALSE;
}
/*
* Short-circuit if nothing to write.
*/
if (d->outtop == 0)
return TRUE;
/*
* Snoop-o-rama.
*/
if (d->snoop_by) {
if (d->character)
write_to_buffer(d->snoop_by, d->character->name, 0);
write_to_buffer(d->snoop_by, "> ", 2);
write_to_buffer(d->snoop_by, d->outbuf, d->outtop);
}
/*
* OS-dependent output.
*/
if (!write_to_descriptor(d->descriptor, d->outbuf, d->outtop)) {
d->outtop = 0;
return FALSE;
}
else {
if (ga)
write_to_descriptor(d->descriptor, go_ahead_str, 0);
d->outtop = 0;
return TRUE;
}
}
void percent_hp(CHAR_DATA *ch, char buf[MAX_STRING_LENGTH])
{
if (ch->hit >= 0)
snprintf(buf, sizeof(buf), "%d%%",
((100 * ch->hit) / UMAX(1,ch->max_hit)));
else
strnzcpy(buf, sizeof(buf), "BAD!");
}
/*
* Bust a prompt (player settable prompt)
* coded by Morgenes for Aldara Mud
* bust
*/
void bust_a_prompt(CHAR_DATA *ch)
{
char buf[MAX_STRING_LENGTH];
char buf2[MAX_STRING_LENGTH];
const char *str;
const char *i;
char *point;
CHAR_DATA *victim;
EXIT_DATA *pexit;
bool found;
const char *dir_name[] = {"N","E","S","W","U","D"};
int door;
if (IS_SET(ch->comm, COMM_AFK)) {
char_printf(ch, "{c<AFK>{x %s", ch->prefix);
return;
}
point = buf;
str = ch->prompt;
if (IS_NULLSTR(str))
str = DEFAULT_PROMPT;
while (*str != '\0') {
if (*str != '%') {
*point++ = *str++;
continue;
}
switch(*++str) {
default:
i = "";
break;
case 't':
snprintf(buf2, sizeof(buf2),
"%d%s", (time_info.hour % 12 == 0) ?
12 : time_info.hour % 12,
time_info.hour >= 12 ? "pm" : "am");
i = buf2;
break;
case 'e':
found = FALSE;
buf2[0] = '\0';
for (door = 0; door < 6; door++)
if ((pexit = ch->in_room->exit[door])
&& pexit->to_room.r
&& can_see_room(ch, pexit->to_room.r)
&& check_blind_raw(ch)
&& (!IS_SET(pexit->exit_info, EX_CLOSED) ||
IS_IMMORTAL(ch))) {
found = TRUE;
strnzcat(buf2, sizeof(buf2),
dir_name[door]);
if (IS_SET(pexit->exit_info, EX_CLOSED))
strnzcat(buf2, sizeof(buf2),
"*");
}
if (buf2[0])
strnzcat(buf2, sizeof(buf2), " ");
i = buf2;
break;
case 'n':
i = ch->name;
break;
case 'S':
i = ch->sex == SEX_MALE ? "Male" :
ch->sex == SEX_FEMALE ? "Female" :
"None";
break;
case 'y':
percent_hp(ch, buf2);
i = buf2;
break;
case 'o':
if ((victim = ch->fighting) != NULL) {
if (can_see(ch, victim)) {
percent_hp(victim, buf2);
i = buf2;
}
else
i = "???";
}
else
i = "None";
break;
case 'h':
snprintf(buf2, sizeof(buf2), "%d", ch->hit);
i = buf2;
break;
case 'H':
snprintf(buf2, sizeof(buf2), "%d", ch->max_hit);
i = buf2;
break;
case 'm':
snprintf(buf2, sizeof(buf2), "%d", ch->mana);
i = buf2;
break;
case 'M':
snprintf(buf2, sizeof(buf2), "%d", ch->max_mana);
i = buf2;
break;
case 'v':
snprintf(buf2, sizeof(buf2), "%d", ch->move);
i = buf2;
break;
case 'V':
snprintf(buf2, sizeof(buf2), "%d", ch->max_move);
i = buf2;
break;
case 'x':
snprintf(buf2, sizeof(buf2), "%d", ch->exp);
i = buf2;
break;
case 'X':
if (!IS_NPC(ch)) {
snprintf(buf2, sizeof(buf2), "%d",
exp_to_level(ch));
i = buf2;
}
else
i = "";
break;
case 'g':
snprintf(buf2, sizeof(buf2), "%d", ch->gold);
i = buf2;
break;
case 's':
snprintf(buf2, sizeof(buf2), "%d", ch->silver);
i = buf2;
break;
case 'a':
i = IS_GOOD(ch) ? "good" :
IS_EVIL(ch) ? "evil" :
"neutral";
break;
case 'r':
if (ch->in_room)
i = (check_blind_raw(ch) && !room_is_dark(ch)) ?
mlstr_cval(ch->in_room->name, ch) :
"darkness";
else
i = "";
break;
case 'R':
if (IS_IMMORTAL(ch) && ch->in_room) {
snprintf(buf2, sizeof(buf2), "%d",
ch->in_room->vnum);
i = buf2;
}
else
i = "";
break;
case 'z':
if (IS_IMMORTAL(ch) && ch->in_room != NULL)
i = ch->in_room->area->name;
else
i = "";
break;
case '%':
i = "%%";
break;
case 'E':
i = OLCED(ch) ? OLCED(ch)->name : str_empty;
if (!IS_NULLSTR(i)) {
snprintf(buf2, sizeof(buf2), "%s ", i);
i = buf2;
}
break;
case 'W':
if (ch->invis_level) {
snprintf(buf2, sizeof(buf2), "Wizi %d ",
ch->invis_level);
i = buf2;
}
else
i = "";
break;
case 'I':
if (ch->incog_level) {
snprintf(buf2, sizeof(buf2), "Incog %d ",
ch->incog_level);
i = buf2;
}
else
i = "";
break;
}
++str;
while((*point = *i) != '\0')
++point, ++i;
}
*point = '\0';
send_to_char(buf, ch);
if (ch->prefix[0] != '\0')
write_to_buffer(ch->desc, ch->prefix, 0);
}
/*
* Append onto an output buffer.
*/
void write_to_buffer(DESCRIPTOR_DATA *d, const char *txt, uint length)
{
uint size;
int i;
bool noiac = (d->connected == CON_PLAYING &&
d->character != NULL &&
IS_SET(d->character->comm, COMM_NOIAC));
/*
* Find length in case caller didn't.
*/
if (length <= 0)
length = strlen(txt);
/*
* Adjust size in case of IACs (they will be doubled)
*/
size = length;
if (!noiac)
for (i = 0; i < length; i++)
if (d->codepage->to[(unsigned char) txt[i]] == IAC)
size++;
/*
* Initial \n\r if needed.
*/
if (d->outtop == 0
&& !d->fcommand
&& (!d->character || !IS_SET(d->character->comm, COMM_TELNET_GA))) {
d->outbuf[0] = '\n';
d->outbuf[1] = '\r';
d->outtop = 2;
}
/*
* Expand the buffer as needed.
*/
while (d->outtop + size >= d->outsize) {
char *outbuf;
if (d->outsize >= 32000) {
bug("Buffer overflow. Closing.\n\r",0);
close_descriptor(d);
return;
}
outbuf = malloc(2 * d->outsize);
strncpy(outbuf, d->outbuf, d->outtop);
free(d->outbuf);
d->outbuf = outbuf;
d->outsize *= 2;
}
/*
* Copy.
*/
while (length--) {
unsigned char c;
c = d->codepage->to[(unsigned char) *txt++];
d->outbuf[d->outtop] = c;
if (c == IAC)
if (noiac)
d->outbuf[d->outtop] = IAC_REPL;
else
d->outbuf[++d->outtop] = IAC;
d->outtop++;
}
return;
}
/*
* Lowest level output function.
* Write a block of text to the file descriptor.
* If this gives errors on very long blocks (like 'ofind all'),
* try lowering the max block size.
*/
bool write_to_descriptor(int desc, char *txt, uint length)
{
uint iStart;
uint nWrite;
uint nBlock;
if (!length)
length = strlen(txt);
for (iStart = 0; iStart < length; iStart += nWrite) {
nBlock = UMIN(length - iStart, 4096);
#if !defined( WIN32 )
if ((nWrite = write(desc, txt + iStart, nBlock)) < 0) {
#else
if ((nWrite = send(desc, txt + iStart, nBlock, 0)) < 0) {
#endif
log_printf("write_to_descriptor: %s", strerror(errno));
return FALSE;
}
}
return TRUE;
}
int search_sockets(DESCRIPTOR_DATA *inp)
{
DESCRIPTOR_DATA *d;
if (IS_IMMORTAL(inp->character))
return 0;
for(d = descriptor_list; d; d = d->next) {
if(!strcmp(inp->host, d->host)) {
if (d->character && inp->character
&& !strcmp(inp->character->name, d->character->name))
continue;
return 1;
}
}
return 0;
}
int align_restrict(CHAR_DATA *ch);
int ethos_check(CHAR_DATA *ch);
void advance(CHAR_DATA *victim, int level);
static void print_hometown(CHAR_DATA *ch)
{
race_t *r;
class_t *cl;
int htn;
if ((r = race_lookup(ORG_RACE(ch))) == NULL
|| !r->pcdata
|| (cl = class_lookup(ch->class)) == NULL) {
char_puts("You should create your character anew.\n", ch);
close_descriptor(ch->desc);
return;
}
if ((htn = hometown_permanent(ch)) >= 0) {
ch->hometown = htn;
char_printf(ch, "\nYour hometown is %s, permanently.\n"
"[Hit Return to continue]\n",
hometown_name(htn));
/* XXX */
ch->endur = 100;
ch->desc->connected = CON_GET_ETHOS;
return;
}
char_puts("\n", ch);
do_help(ch, "HOMETOWN");
hometown_print_avail(ch);
char_puts("? ", ch);
ch->desc->connected = CON_PICK_HOMETOWN;
}
/*
* Deal with sockets that haven't logged in yet.
*/
void nanny(DESCRIPTOR_DATA *d, const char *argument)
{
char buf[MAX_STRING_LENGTH];
DESCRIPTOR_DATA *d_old, *d_next;
char buf1[MAX_STRING_LENGTH];
char arg[MAX_INPUT_LENGTH];
CHAR_DATA *ch;
char *pwdnew;
int iClass,race,i;
int nextquest = 0;
struct sockaddr_in sock;
int size;
race_t *r;
while (isspace(*argument))
argument++;
ch = d->character;
switch (d->connected) {
default:
bug("Nanny: bad d->connected %d.", d->connected);
close_descriptor(d);
return;
case CON_GET_CODEPAGE: {
int num;
if (argument[0] == '\0') {
close_descriptor(d);
return;
}
if (argument[1] != '\0'
|| (num = argument[0] - '1') < 0
|| num >= NCODEPAGES) {
cp_print(d);
break;
}
d->codepage = codepages+num;
log_printf("'%s' codepage selected", d->codepage->name);
d->connected = CON_GET_NAME;
write_to_buffer(d, "By which name do you wish to be known? ", 0);
break;
}
case CON_GET_NAME:
if (argument[0] == '\0') {
close_descriptor(d);
return;
}
if (!pc_name_ok(argument)) {
write_to_buffer(d, "Illegal name, try another.\n\r"
"Name: ", 0);
return;
}
load_char_obj(d, argument);
ch = d->character;
if (d->host == NULL) {
size = sizeof(sock);
if (getpeername(d->descriptor,
(struct sockaddr *) &sock, &size) < 0)
d->host = str_dup("(unknown)");
else {
#if defined (WIN32)
printf("%s@%s\n", ch->name, inet_ntoa(sock.sin_addr));
#else
fprintf(rfout, "%s@%s\n",
ch->name, inet_ntoa(sock.sin_addr));
#endif
d->connected = CON_RESOLV;
/* wait until sock.sin_addr gets resolved */
break;
}
}
/* FALLTHRU */
case CON_RESOLV:
if (d->host == NULL)
break;
/*
* Swiftest: I added the following to ban sites. I don't
* endorse banning of sites, but Copper has few descriptors now
* and some people from certain sites keep abusing access by
* using automated 'autodialers' and leaving connections hanging.
*
* Furey: added suffix check by request of Nickel of HiddenWorlds.
*/
if (check_ban(d->host,BAN_ALL)) {
write_to_buffer(d, "Your site has been banned from this mud.\n\r", 0);
close_descriptor(d);
return;
}
if (!IS_IMMORTAL(ch)) {
if (check_ban(d->host,BAN_PLAYER)) {
write_to_buffer(d,"Your site has been banned for players.\n\r",0);
close_descriptor(d);
return;
}
#undef NO_PLAYING_TWICE
#ifdef NO_PLAYING_TWICE
if(search_sockets(d)) {
write_to_buffer(d, "Playing twice is restricted...\n\r", 0);
close_descriptor(d);
return;
}
#endif
if (iNumPlayers > MAX_OLDIES && !IS_SET(ch->plr_flags, PLR_NEW)) {
snprintf(buf, sizeof(buf),
"\n\rThere are currently %i players mudding out of a maximum of %i.\n\rPlease try again soon.\n\r",iNumPlayers - 1, MAX_OLDIES);
write_to_buffer(d, buf, 0);
close_descriptor(d);
return;
}
if (iNumPlayers > MAX_NEWBIES && IS_SET(ch->plr_flags, PLR_NEW)) {
snprintf(buf, sizeof(buf),
"\n\rThere are currently %i players mudding. New player creation is \n\rlimited to when there are less than %i players. Please try again soon.\n\r",
iNumPlayers - 1, MAX_NEWBIES);
write_to_buffer(d, buf, 0);
close_descriptor(d);
return;
}
}
if (IS_SET(ch->plr_flags, PLR_DENY))
{
log_printf("Denying access to %s@%s.", argument, d->host);
write_to_buffer(d, "You are denied access.\n\r", 0);
close_descriptor(d);
return;
}
if (check_reconnect(d, argument, FALSE))
REMOVE_BIT(ch->plr_flags, PLR_NEW);
else if (wizlock && !IS_HERO(ch)) {
write_to_buffer(d, "The game is wizlocked.\n\r", 0);
close_descriptor(d);
return;
}
if (!IS_SET(ch->plr_flags, PLR_NEW)) {
/* Old player */
write_to_descriptor(d->descriptor, echo_off_str, 0);
write_to_buffer(d, "Password: ", 0);
d->connected = CON_GET_OLD_PASSWORD;
return;
}
else {
/* New player */
if (newlock) {
write_to_buffer(d, "The game is newlocked.\n\r", 0);
close_descriptor(d);
return;
}
if (check_ban(d->host, BAN_NEWBIES)) {
write_to_buffer(d, "New players are not allowed from your site.\n\r", 0);
close_descriptor(d);
return;
}
do_help(ch, "NAME");
d->connected = CON_CONFIRM_NEW_NAME;
return;
}
break;
/* RT code for breaking link */
case CON_BREAK_CONNECT:
switch(*argument) {
case 'y' : case 'Y':
for (d_old = descriptor_list; d_old; d_old = d_next) {
CHAR_DATA *rch;
d_next = d_old->next;
if (d_old == d || d_old->character == NULL)
continue;
rch = d_old->original ? d_old->original :
d_old->character;
if (str_cmp(ch->name, rch->name))
continue;
if (d_old->original)
do_return(d_old->character, str_empty);
close_descriptor(d_old);
}
if (check_reconnect(d, ch->name, TRUE))
return;
write_to_buffer(d,"Reconnect attempt failed.\n\r",0);
/* FALLTHRU */
case 'n' : case 'N':
write_to_buffer(d,"Name: ",0);
if (d->character != NULL) {
free_char(d->character);
d->character = NULL;
}
d->connected = CON_GET_NAME;
break;
default:
write_to_buffer(d, "Please type Y or N? ", 0);
break;
}
break;
case CON_CONFIRM_NEW_NAME:
switch (*argument) {
case 'y': case 'Y':
snprintf(buf, sizeof(buf),
"New character.\n\r"
"Give me a password for %s: ", ch->name);
write_to_buffer(d, buf, 0);
write_to_descriptor(d->descriptor, echo_off_str, 0);
d->connected = CON_GET_NEW_PASSWORD;
break;
case 'n': case 'N':
write_to_buffer(d, "Ok, what IS it, then? ", 0);
free_char(d->character);
d->character = NULL;
d->connected = CON_GET_NAME;
break;
default:
write_to_buffer(d, "Please type Yes or No? ", 0);
break;
}
break;
case CON_GET_NEW_PASSWORD:
#if defined(unix)
write_to_buffer( d, "\n\r", 2 );
#endif
if (strlen(argument) < 5) {
write_to_buffer(d, "Password must be at least five characters long.\n\rPassword: ", 0);
return;
}
pwdnew = crypt(argument, ch->name);
free_string(ch->pcdata->pwd);
ch->pcdata->pwd = str_dup(pwdnew);
write_to_buffer(d, "Please retype password: ", 0);
d->connected = CON_CONFIRM_NEW_PASSWORD;
break;
case CON_CONFIRM_NEW_PASSWORD:
#if defined(unix)
write_to_buffer( d, "\n\r", 2 );
#endif
if (strcmp(crypt(argument, ch->pcdata->pwd), ch->pcdata->pwd)) {
write_to_buffer(d, "Passwords don't match.\n\r"
"Retype password: ", 0);
d->connected = CON_GET_NEW_PASSWORD;
return;
}
write_to_descriptor(d->descriptor, (char *) echo_on_str, 0);
write_to_buffer(d, "The Shades of Gray is home for the following races:\n\r", 0);
do_help(ch, "RACETABLE");
d->connected = CON_GET_NEW_RACE;
break;
case CON_GET_NEW_RACE:
one_argument(argument, arg, sizeof(arg));
if (!str_cmp(arg, "help")) {
argument = one_argument(argument, arg, sizeof(arg));
if (argument[0] == '\0') {
write_to_buffer(d, "The Shades of Gray is the home for the following races:\n\r", 0);
do_help(ch,"RACETABLE");
}
else {
do_help(ch, argument);
write_to_buffer(d, "What is your race? ('help <race>' for more information) ",0);
}
break;
}
race = rn_lookup(argument);
r = RACE(race);
if (race == 0 || !r->pcdata) {
write_to_buffer(d, "That is not a valid race.\n\r", 0);
write_to_buffer(d, "The following races are available:\n\r ", 0);
for (race = 1; race < races.nused; race++) {
r = RACE(race);
if (!r->pcdata)
break;
if (race == 8 || race == 14)
write_to_buffer(d,"\n\r ",0);
write_to_buffer(d,"(",0);
write_to_buffer(d, r->name, 0);
write_to_buffer(d,") ",0);
}
write_to_buffer(d, "\n\r", 0);
write_to_buffer(d, "What is your race? ('help <race>' for more information) ", 0);
break;
}
SET_ORG_RACE(ch, race);
ch->race = race;
for (i=0; i < MAX_STATS;i++)
ch->mod_stat[i] = 0;
/* Add race stat modifiers
for (i = 0; i < MAX_STATS; i++)
ch->mod_stat[i] += r->pcdata->stats[i]; */
/* Add race modifiers */
ch->max_hit += r->pcdata->hp_bonus;
ch->hit = ch->max_hit;
ch->max_mana += r->pcdata->mana_bonus;
ch->mana = ch->max_mana;
ch->practice = r->pcdata->prac_bonus;
ch->affected_by = ch->affected_by| r->aff;
ch->imm_flags = ch->imm_flags| r->imm;
ch->res_flags = ch->res_flags| r->res;
ch->vuln_flags = ch->vuln_flags| r->vuln;
ch->form = r->form;
ch->parts = r->parts;
/* add cost */
ch->pcdata->points = r->pcdata->points;
ch->size = r->pcdata->size;
write_to_buffer(d, "What is your sex (M/F)? ", 0);
d->connected = CON_GET_NEW_SEX;
break;
case CON_GET_NEW_SEX:
switch (argument[0])
{
case 'm': case 'M': ch->sex = SEX_MALE;
ch->pcdata->true_sex = SEX_MALE;
break;
case 'f': case 'F': ch->sex = SEX_FEMALE;
ch->pcdata->true_sex = SEX_FEMALE;
break;
default:
write_to_buffer(d, "That's not a sex.\n\rWhat IS your sex? ", 0);
return;
}
do_help(ch,"class help");
strnzcpy(buf, sizeof(buf), "Select a class:\n\r[ ");
snprintf(buf1, sizeof(buf), " (Continuing:) ");
for (iClass = 0; iClass < classes.nused; iClass++)
{
if (class_ok(ch,iClass))
{
if (iClass < 7)
{
strnzcat(buf, sizeof(buf), CLASS(iClass)->name);
strnzcat(buf, sizeof(buf), " ");
}
else
{
strnzcat(buf1, sizeof(buf), CLASS(iClass)->name);
strnzcat(buf1, sizeof(buf), " ");
}
}
}
strnzcat(buf, sizeof(buf), "\n\r");
strnzcat(buf1, sizeof(buf), "]:\n\r");
write_to_buffer(d, buf, 0);
write_to_buffer(d, buf1, 0);
write_to_buffer(d,
"What is your class ('help <class>' for more information)? ",0);
d->connected = CON_GET_NEW_CLASS;
break;
case CON_GET_NEW_CLASS:
iClass = cn_lookup(argument);
argument = one_argument(argument, arg, sizeof(arg));
if (!str_cmp(arg,"help"))
{
if (argument[0] == '\0')
do_help(ch,"class help");
else
do_help(ch,argument);
write_to_buffer(d,
"What is your class ('help <class>' for more information)? ",0);
return;
}
if (iClass == -1)
{
write_to_buffer(d,
"That's not a class.\n\rWhat IS your class? ", 0);
return;
}
if (!class_ok(ch,iClass))
{
write_to_buffer(d,
"That class is not available for your race or sex.\n\rChoose again: ",0);
return;
}
ch->class = iClass;
ch->pcdata->points += CLASS(iClass)->points;
act("You are now $t.", ch, CLASS(iClass)->name, NULL, TO_CHAR);
for (i = 0; i < MAX_STATS; i++)
ch->perm_stat[i] = number_range(10, get_max_train(ch, i));
snprintf(buf, sizeof(buf),
"Str:%s Int:%s Wis:%s Dex:%s Con:%s Cha:%s\n\rAccept (Y/N)? ",
get_stat_alias(ch, STAT_STR),
get_stat_alias(ch, STAT_INT),
get_stat_alias(ch, STAT_WIS),
get_stat_alias(ch, STAT_DEX),
get_stat_alias(ch, STAT_CON),
get_stat_alias(ch, STAT_CHA));
do_help(ch, "stats");
write_to_buffer(d, "\n\rNow rolling for your stats (10-20+).\n\r", 0);
write_to_buffer(d, "You don't get many trains, so choose well.\n\r", 0);
write_to_buffer(d, buf, 0);
d->connected = CON_ACCEPT_STATS;
break;
case CON_ACCEPT_STATS:
switch(argument[0])
{
case 'H': case 'h': case '?':
do_help(ch,"stats");
break;
case 'y': case 'Y':
for (i=0; i < MAX_STATS;i++)
ch->mod_stat[i] = 0;
write_to_buffer(d, "\n\r", 2);
if (!align_restrict(ch))
{
write_to_buffer(d, "You may be good, neutral, or evil.\n\r",0);
write_to_buffer(d, "Which alignment (G/N/E)? ",0);
d->connected = CON_GET_ALIGNMENT;
}
else {
write_to_buffer(d, "[Hit Return to Continue]\n\r",0);
print_hometown(ch);
}
break;
case 'n': case 'N':
for (i = 0; i < MAX_STATS; i++)
ch->perm_stat[i] = number_range(10, get_max_train(ch, i));
snprintf(buf, sizeof(buf),
"Str:%s Int:%s Wis:%s Dex:%s Con:%s Cha:%s\n\rAccept (Y/N)? ",
get_stat_alias(ch, STAT_STR),
get_stat_alias(ch, STAT_INT),
get_stat_alias(ch, STAT_WIS),
get_stat_alias(ch, STAT_DEX),
get_stat_alias(ch, STAT_CON),
get_stat_alias(ch, STAT_CHA));
write_to_buffer(d, buf,0);
d->connected = CON_ACCEPT_STATS;
break;
default:
write_to_buffer(d,"Please answer (Y/N)? ",0);
break;
}
break;
case CON_GET_ALIGNMENT:
switch(argument[0])
{
case 'g' : case 'G' :
ch->alignment = 1000;
write_to_buffer(d, "Now your character is good.\n\r",0);
break;
case 'n' : case 'N' :
ch->alignment = 0;
write_to_buffer(d, "Now your character is neutral.\n\r",0);
break;
case 'e' : case 'E' :
ch->alignment = -1000;
write_to_buffer(d, "Now your character is evil.\n\r",0);
break;
default:
write_to_buffer(d,"That's not a valid alignment.\n\r",0);
write_to_buffer(d,"Which alignment (G/N/E)? ",0);
return;
}
write_to_buffer(d, "\n\r[Hit Return to Continue]\n\r", 0);
print_hometown(ch);
break;
case CON_PICK_HOMETOWN: {
int htn;
if (argument[0] == '\0'
|| (htn = htn_lookup(argument)) < 0
|| hometown_restrict(HOMETOWN(htn), ch)) {
char_puts("That's not a valid hometown.\n", ch);
print_hometown(ch);
return;
}
ch->hometown = htn;
char_printf(ch, "\nNow your hometown is %s.\n"
"[Hit Return to continue]\n",
hometown_name(htn));
ch->endur = 100;
d->connected = CON_GET_ETHOS;
break;
}
case CON_GET_ETHOS:
if (!ch->endur)
{
switch(argument[0])
{
case 'H': case 'h': case '?':
do_help(ch, "alignment"); return; break;
case 'L': case 'l':
snprintf(buf, sizeof(buf), "\n\rNow you are lawful-%s.\n\r",
IS_GOOD(ch) ? "good" : IS_EVIL(ch) ? "evil" : "neutral");
write_to_buffer(d, buf, 0);
ch->ethos = ETHOS_LAWFUL;
break;
case 'N': case 'n':
snprintf(buf, sizeof(buf), "\n\rNow you are neutral-%s.\n\r",
IS_GOOD(ch) ? "good" : IS_EVIL(ch) ? "evil" : "neutral");
write_to_buffer(d, buf, 0);
ch->ethos = ETHOS_NEUTRAL;
break;
case 'C': case 'c':
snprintf(buf, sizeof(buf), "\n\rNow you are chaotic-%s.\n\r",
IS_GOOD(ch) ? "good" : IS_EVIL(ch) ? "evil" : "neutral");
write_to_buffer(d, buf, 0);
ch->ethos = ETHOS_CHAOTIC;
break;
default:
write_to_buffer(d, "\n\rThat is not a valid ethos.\n\r", 0);
write_to_buffer(d, "What ethos do you want, (L/N/C) <type help for more info>? ",0);
return;
}
} else {
ch->endur = 0;
if (!ethos_check(ch)) {
write_to_buffer(d, "What ethos do you want, (L/N/C) "
"<type help for more info> ?", 0);
d->connected = CON_GET_ETHOS;
return;
} else
ch->ethos = 1;
}
write_to_buffer(d, "\n\r[Hit Return to Continue]\n\r",0);
d->connected = CON_CREATE_DONE;
break;
case CON_CREATE_DONE:
log_printf("%s@%s new player.", ch->name, d->host);
write_to_buffer(d, "\n\r", 2);
do_help(ch, "motd");
char_puts("[Press Enter to continue]", ch);
d->connected = CON_READ_MOTD;
break;
case CON_GET_OLD_PASSWORD:
write_to_buffer(d, "\n\r", 2);
if (strcmp(crypt(argument, ch->pcdata->pwd), ch->pcdata->pwd)) {
write_to_buffer(d, "Wrong password.\n\r", 0);
log_printf("Wrong password by %s@%s",
ch->name, d->host);
if (ch->endur == 2)
close_descriptor(d);
else {
write_to_descriptor(d->descriptor,
(char *) echo_off_str, 0);
write_to_buffer(d, "Password: ", 0);
d->connected = CON_GET_OLD_PASSWORD;
ch->endur++;
}
return;
}
if (ch->pcdata->pwd[0] == '\0') {
write_to_buffer(d, "Warning! Null password!\n\r"
"Type 'password null <new password>'"
" to fix.\n\r", 0);
}
write_to_descriptor(d->descriptor, (char *) echo_on_str, 0);
if (check_playing(d, ch->name)
|| check_reconnect(d, ch->name, TRUE))
return;
log_printf("%s@%s has connected.", ch->name, d->host);
d->connected = CON_READ_IMOTD;
/* FALL THRU */
case CON_READ_IMOTD:
if (IS_HERO(ch))
do_help(ch, "imotd");
write_to_buffer(d,"\n\r",2);
do_help(ch, "motd");
d->connected = CON_READ_MOTD;
/* FALL THRU */
case CON_READ_MOTD:
update_skills(ch);
write_to_buffer(d,
"\n\rWelcome to Shades of Gray Multi User Dungeon. Enjoy!!...\n\r",
0);
ch->next = char_list;
char_list = ch;
if (!char_list_lastpc)
char_list_lastpc = ch;
d->connected = CON_PLAYING;
{
int count;
FILE *max_on_file;
int tmp = 0;
count = 0;
for (d = descriptor_list; d != NULL; d = d->next)
if (d->connected == CON_PLAYING)
count++;
max_on = UMAX(count, max_on);
if ((max_on_file = dfopen(TMP_PATH, MAXON_FILE, "r"))) {
fscanf(max_on_file, "%d", &tmp);
fclose(max_on_file);
}
if (tmp < max_on
&& (max_on_file = dfopen(TMP_PATH, MAXON_FILE, "w"))) {
fprintf(max_on_file, "%d", max_on);
log("Global max_on changed.");
fclose(max_on_file);
}
}
reset_char(ch);
/* quest code */
nextquest = -abs(ch->pcdata->questtime);
quest_cancel(ch);
ch->pcdata->questtime = nextquest;
/* !quest code */
wiznet("{W$N{x has left real life behind.",
ch, NULL, WIZ_LOGINS, 0, ch->level);
for (i = 0; i < MAX_STATS; i++) {
int max_stat = get_max_train(ch, i);
if (ch->perm_stat[i] > max_stat) {
ch->train += ch->perm_stat[i] - max_stat;
ch->perm_stat[i] = max_stat;
}
}
if (ch->gold > 6000 && !IS_IMMORTAL(ch)) {
char_printf(ch, "You are taxed %d gold to pay for the Mayor's bar.\n\r", (ch->gold - 6000) / 2);
ch->gold -= (ch->gold - 6000) / 2;
}
if (!IS_IMMORTAL(ch)) {
for (i = 2; exp_for_level(ch, i) < ch->exp; i++)
;
if (i < ch->level) {
int con;
int wis;
int inte;
int dex;
con = ch->perm_stat[STAT_CON];
wis = ch->perm_stat[STAT_WIS];
inte = ch->perm_stat[STAT_INT];
dex = ch->perm_stat[STAT_DEX];
ch->perm_stat[STAT_CON] = get_max_train(ch, STAT_CON);
ch->perm_stat[STAT_WIS] = get_max_train(ch, STAT_WIS);
ch->perm_stat[STAT_INT] = get_max_train(ch, STAT_INT);
ch->perm_stat[STAT_DEX] = get_max_train(ch, STAT_DEX);
do_remove(ch, "all");
advance(ch, i-1);
ch->perm_stat[STAT_CON] = con;
ch->perm_stat[STAT_WIS] = wis;
ch->perm_stat[STAT_INT] = inte;
ch->perm_stat[STAT_DEX] = dex;
}
}
if (ch->level == 0) {
OBJ_DATA *wield;
OBJ_INDEX_DATA *map;
ch->level = 1;
ch->exp = base_exp(ch);
ch->hit = ch->max_hit;
ch->mana = ch->max_mana;
ch->move = ch->max_move;
ch->train = 3;
ch->practice += 5;
ch->pcdata->death = 0;
set_title(ch, title_lookup(ch));
do_outfit(ch, str_empty);
obj_to_char(create_obj(get_obj_index(OBJ_VNUM_MAP), 0), ch);
obj_to_char(create_obj(get_obj_index(OBJ_VNUM_NMAP1), 0), ch);
obj_to_char(create_obj(get_obj_index(OBJ_VNUM_NMAP2), 0), ch);
if ((map = get_map(ch)) != NULL)
obj_to_char(create_obj(map, 0), ch);
if ((wield = get_eq_char(ch, WEAR_WIELD)))
set_skill_raw(ch, get_weapon_sn(wield),
40, FALSE);
char_puts("\n", ch);
do_help(ch, "NEWBIE INFO");
char_puts("\n", ch);
char_to_room(ch, get_room_index(ROOM_VNUM_SCHOOL));
}
else {
CHAR_DATA *pet;
ROOM_INDEX_DATA *to_room;
if (ch->in_room
&& (room_is_private(ch->in_room) ||
(ch->in_room->area->clan &&
ch->in_room->area->clan != ch->clan)))
ch->in_room = NULL;
if (ch->in_room)
to_room = ch->in_room;
else if (IS_IMMORTAL(ch))
to_room = get_room_index(ROOM_VNUM_CHAT);
else
to_room = get_room_index(ROOM_VNUM_TEMPLE);
pet = ch->pet;
act("$N has entered the game.",
to_room->people, NULL, ch, TO_ALL);
char_to_room(ch, to_room);
if (pet) {
act("$N has entered the game.",
to_room->people, NULL, pet, TO_ROOM);
char_to_room(pet, to_room);
}
}
if (!JUST_KILLED(ch)) {
do_look(ch, "auto");
do_unread(ch, "login");
}
break;
}
}
/*
* look for link-dead player to reconnect.
*
* when fConn == FALSE then
* simple copy password for newly [re]connected character
* authentication
*
* otherwise reconnect attempt is made
*/
bool check_reconnect(DESCRIPTOR_DATA *d, const char *name, bool fConn)
{
CHAR_DATA *ch;
DESCRIPTOR_DATA *d2;
if (!fConn) {
for (d2 = descriptor_list; d2; d2 = d2->next) {
if (d2 == d)
continue;
ch = d2->original ? d2->original : d2->character;
if (ch && !str_cmp(d->character->name, ch->name)) {
free_string(d->character->pcdata->pwd);
d->character->pcdata->pwd = str_qdup(ch->pcdata->pwd);
return TRUE;
}
}
}
for (ch = char_list; ch && !IS_NPC(ch); ch = ch->next) {
if ((!fConn || ch->desc == NULL)
&& !str_cmp(d->character->name, ch->name)) {
if (!fConn) {
free_string(d->character->pcdata->pwd);
d->character->pcdata->pwd = str_qdup(ch->pcdata->pwd);
}
else {
free_char(d->character);
d->character = ch;
ch->desc = d;
ch->timer = 0;
char_puts("Reconnecting. Type replay to see missed tells.\n", ch);
act("$n has reconnected.",
ch, NULL, NULL, TO_ROOM);
log_printf("%s@%s reconnected.",
ch->name, d->host);
wiznet("$N groks the fullness of $S link.",
ch, NULL, WIZ_LINKS, 0, 0);
d->connected = CON_PLAYING;
}
return TRUE;
}
}
return FALSE;
}
/*
* Check if already playing.
*/
bool check_playing(DESCRIPTOR_DATA *d, const char *name)
{
DESCRIPTOR_DATA *dold;
for (dold = descriptor_list; dold; dold = dold->next) {
if (dold != d
&& dold->character != NULL
&& dold->connected != CON_GET_CODEPAGE
&& dold->connected != CON_GET_NAME
&& dold->connected != CON_RESOLV
&& dold->connected != CON_GET_OLD_PASSWORD
&& !str_cmp(name, dold->original ? dold->original->name :
dold->character->name)) {
write_to_buffer(d, "That character is already playing.\n\r",0);
write_to_buffer(d, "Do you wish to connect anyway (Y/N)?",0);
d->connected = CON_BREAK_CONNECT;
return TRUE;
}
}
return FALSE;
}
void stop_idling(CHAR_DATA *ch)
{
if (ch == NULL
|| ch->desc == NULL
|| ch->desc->connected != CON_PLAYING
|| !ch->was_in_room
|| ch->in_room->vnum != ROOM_VNUM_LIMBO)
return;
ch->timer = 0;
char_from_room(ch);
act("$N has returned from the void.",
ch->was_in_room->people, NULL, ch, TO_ALL);
char_to_room(ch, ch->was_in_room);
ch->was_in_room = NULL;
}
void char_puts(const char *txt, CHAR_DATA *ch)
{
send_to_char(GETMSG(txt, ch->lang), ch);
}
void char_printf(CHAR_DATA *ch, const char *format, ...)
{
char buf[MAX_STRING_LENGTH];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), GETMSG(format, ch->lang), ap);
va_end(ap);
send_to_char(buf, ch);
}
/*
* Write to one char.
*/
void send_to_char(const char *txt, CHAR_DATA *ch)
{
char buf[MAX_STRING_LENGTH*4];
if (txt == NULL || ch->desc == NULL)
return;
parse_colors(txt, buf, sizeof(buf), OUTPUT_FORMAT(ch));
write_to_buffer(ch->desc, buf, 0);
}
/*
* Send a page to one char.
*/
void page_to_char(const char *txt, CHAR_DATA *ch)
{
if (txt == NULL || ch->desc == NULL)
return; /* ben yazdim ibrahim */
if (ch->lines == 0) {
send_to_char(txt, ch);
return;
}
ch->desc->showstr_head = str_dup(txt);
ch->desc->showstr_point = ch->desc->showstr_head;
show_string(ch->desc, str_empty);
}
/* string pager */
void show_string(struct descriptor_data *d, char *input)
{
char buffer[4*MAX_STRING_LENGTH];
char buf[MAX_INPUT_LENGTH];
char *scan;
int lines = 0;
int show_lines;
one_argument(input, buf, sizeof(buf));
if (buf[0] != '\0') {
if (d->showstr_head) {
free_string(d->showstr_head);
d->showstr_head = NULL;
}
d->showstr_point = NULL;
return;
}
if (d->character)
show_lines = d->character->lines;
else
show_lines = 0;
for (scan = buffer; scan - buffer < sizeof(buffer)-2;
scan++, d->showstr_point++) {
/*
* simple copy if not eos and not eol
*/
if ((*scan = *d->showstr_point) && (*scan) != '\n')
continue;
/*
* bamf out buffer if we reached eos or show_lines limit
*/
if (!*scan || (show_lines > 0 && ++lines >= show_lines)) {
const char *chk;
if (*scan)
*++scan = '\0';
send_to_char(buffer, d->character);
for (chk = d->showstr_point; isspace(*chk); chk++)
;
if (!*chk) {
if (d->showstr_head) {
free_string(d->showstr_head);
d->showstr_head = NULL;
}
d->showstr_point = NULL;
}
return;
}
}
}
void log_area_popularity(void)
{
FILE *fp;
AREA_DATA *area;
extern AREA_DATA *area_first;
if ((fp = dfopen(TMP_PATH, AREASTAT_FILE, "w")) == NULL)
return;
fprintf(fp,"\nBooted %sArea popularity statistics (in char * ticks)\n",
str_boot_time);
for (area = area_first; area != NULL; area = area->next)
if (area->count >= 5000000)
fprintf(fp,"%-60s overflow\n",area->name);
else
fprintf(fp,"%-60s %u\n",area->name,area->count);
fclose(fp);
}
bool class_ok(CHAR_DATA *ch, int class)
{
race_t *r;
class_t *cl;
if ((cl = class_lookup(class)) == NULL
|| (r = race_lookup(ORG_RACE(ch))) == NULL
|| !r->pcdata)
return FALSE;
if (rclass_lookup(r, cl->name) == NULL
|| (cl->restrict_sex >= 0 && cl->restrict_sex != ch->sex))
return FALSE;
return TRUE;
}
int align_restrict(CHAR_DATA *ch)
{
DESCRIPTOR_DATA *d = ch->desc;
race_t *r;
if ((r = race_lookup(ORG_RACE(ch))) == NULL
|| !r->pcdata)
return RA_NONE;
if (r->pcdata->restrict_align == RA_GOOD
|| CLASS(ch->class)->restrict_align == RA_GOOD) {
write_to_buffer(d, "Your character has good tendencies.\n\r",0);
ch->alignment = 1000;
return RA_GOOD;
}
if (r->pcdata->restrict_align == RA_NEUTRAL
|| CLASS(ch->class)->restrict_align == RA_NEUTRAL) {
write_to_buffer(d, "Your character has neutral tendencies.\n\r",0);
ch->alignment = 0;
return RA_NEUTRAL;
}
if (r->pcdata->restrict_align == RA_EVIL
|| CLASS(ch->class)->restrict_align == RA_EVIL) {
write_to_buffer(d, "Your character has evil tendencies.\n\r",0);
ch->alignment = -1000;
return RA_EVIL;
}
return RA_NONE;
}
int ethos_check(CHAR_DATA *ch)
{
DESCRIPTOR_DATA *d = ch->desc;
class_t *cl;
if ((cl = class_lookup(ch->class))) {
/*
* temporary workaround for paladins
*/
if (IS_SET(cl->restrict_ethos, ETHOS_LAWFUL)) {
write_to_buffer(d, "You are Lawful.\n\r", 0);
return 1;
}
}
return 0;
}
#if !defined (WIN32)
void resolv_done()
{
char *host;
char buf[MAX_STRING_LENGTH];
char *p;
DESCRIPTOR_DATA *d;
while (fgets(buf, sizeof(buf), rfin)) {
if ((p = strchr(buf, '\n')) == NULL) {
log_printf("rfin: line too long, skipping to '\\n'");
while(fgetc(rfin) != '\n')
;
continue;
}
*p = '\0';
if ((host = strchr(buf, '@')) == NULL)
continue;
*host++ = '\0';
log_printf("resolv_done: %s@%s", buf, host);
for (d = descriptor_list; d; d = d->next) {
if (d->host
|| d->character == NULL
|| str_cmp(buf, d->character->name))
continue;
d->host = str_dup(host);
}
}
}
#endif
/* Windows 95 and Windows NT support functions (copied from Envy) */
#if defined (WIN32)
void gettimeofday (struct timeval *tp, void *tzp)
{
tp->tv_sec = time( NULL );
tp->tv_usec = 0;
}
#endif