/**************************************************************
* FFTacticsMUD : main.cpp *
**************************************************************
* (c) 2002 Damien Dailidenas (Trenton). All rights reserved. *
**************************************************************/
#include "main.h"
#include <strstream>
#include <cstdio>
#include <sys/time.h>
#include <csignal>
#include <fcntl.h>
#include <netdb.h>
#include <sys/socket.h>
#include <unistd.h>
void init_signals args(());
void game_loop args((int control));
int init_socket args((int port));
void init_descriptor args((int control));
DESC *desc_list;
CH *ch_list;
AREA *area_list;
BATTLE_AREA *battle_area_list;
BATTLE *battle_list;
bool game_down;
string str_boot_time;
time_t current_time;
int port, control;
extern string login_screen;
int main(int argc, char **argv) {
struct timeval now_time;
bool copyover = false;
gettimeofday(&now_time, NULL);
current_time = (time_t) now_time.tv_sec;
str_boot_time = ctime(¤t_time);
if(argc > 1) {
if(!is_num(argv[1])) {
fprintf(stderr, "Usage: %s [port #]\n", argv[0]);
exit(1);
}
else if((port = atoi(argv[1])) <= 1024) {
fprintf(stderr, "Port number must be above 1024.\n");
exit(1);
}
if(argv[2] && argv[2][0]) {
copyover = true;
control = atoi(argv[3]);
}
else
copyover = false;
}
if(!copyover)
control = init_socket(port);
startup();
init_signals();
if(copyover)
copyover_recover();
game_loop(control);
close(control);
log_string("Normal termination of game.");
exit(0);
return 0;
}
int init_socket(int port) {
static struct sockaddr_in sa_zero;
struct sockaddr_in sa;
int x = 1, fd;
if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
perror("init_socket: socket");
exit(1);
}
if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &x, sizeof(x)) < 0) {
perror("init_socket: SO_REUSEADDR");
close(fd);
exit(1);
}
sa = sa_zero;
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if(bind(fd, (struct sockaddr *) &sa, sizeof(sa) ) < 0) {
perror("Init socket: bind");
close(fd);
exit(1);
}
if(listen(fd, 3) < 0) {
perror("Init socket: listen");
close(fd);
exit(1);
}
return fd;
}
void game_loop(int control) {
static struct timeval null_time;
struct timeval last_time;
DESC *d_next;
signal(SIGPIPE, SIG_IGN);
gettimeofday(&last_time, NULL);
current_time = (time_t) last_time.tv_sec;
while(!game_down) {
fd_set in_set, out_set, exc_set;
DESC *d;
int maxdesc;
FD_ZERO(&in_set);
FD_ZERO(&out_set);
FD_ZERO(&exc_set);
FD_SET(control, &in_set);
maxdesc = control;
for(d = desc_list; d; d = d->next) {
maxdesc = UMAX(maxdesc, d->desc);
FD_SET(d->desc, &in_set);
FD_SET(d->desc, &out_set);
FD_SET(d->desc, &exc_set);
}
if(select(maxdesc + 1, &in_set, &out_set, &exc_set, &null_time) < 0) {
perror("gameLoop: select: poll");
exit(1);
}
if(FD_ISSET(control, &in_set))
init_descriptor(control);
FD_ZERO(&in_set);
FD_ZERO(&out_set);
FD_ZERO(&exc_set);
for(d = desc_list; d; d = d->next) {
maxdesc = UMAX(maxdesc, d->desc);
FD_SET(d->desc, &in_set);
FD_SET(d->desc, &out_set);
FD_SET(d->desc, &exc_set);
}
if(select(maxdesc + 1, &in_set, &out_set, &exc_set, &null_time) < 0) {
perror("gameLoop: select: poll");
exit(1);
}
for(d = desc_list; d; d = d_next) {
d_next = d->next;
if(FD_ISSET(d->desc, &exc_set)) {
FD_CLR(d->desc, &in_set);
FD_CLR(d->desc, &out_set);
if(d->ch && d->connected == CON_PLAYING)
d->ch->save();
d->outbuf = "";
d->close_socket();
}
}
for(d = desc_list; d; d = d_next) {
d_next = d->next;
d->fcommand = false;
if(FD_ISSET(d->desc, &in_set)) {
if(d->ch)
d->ch->timer = 0;
if(!d->read()) {
FD_CLR(d->desc, &out_set);
if(d->ch && d->connected == CON_PLAYING)
d->ch->save();
d->outbuf = "";
d->close_socket();
continue;
}
}
if(d->ch && d->connected == CON_GET_NAME && ++d->ch->timer == 60) {
d->ch->printf("Idle timeout, disconnecting...\n\r");
d->close_socket();
continue;
}
if(d->ch && d->ch->wait) {
--d->ch->wait;
continue;
}
d->read_buffer();
if(!d->incomm.empty()) {
d->fcommand = true;
switch(d->connected) {
case CON_PLAYING:
if(!d->ch->queue[0].empty())
d->ch->update_queue();
else
d->ch->interpret(d->incomm);
break;
default:
d->nanny(d->incomm);
break;
}
d->incomm = "";
}
}
update_handler();
for(d = desc_list; d; d = d_next) {
d_next = d->next;
if((d->fcommand || !d->outbuf.empty()) && FD_ISSET(d->desc, &out_set)) {
if(!d->process_output(true)) {
if(d->ch)
d->ch->save();
d->outbuf = "";
d->close_socket();
}
}
}
{
struct timeval now_time;
long secDelta, usecDelta;
gettimeofday(&now_time, NULL);
usecDelta = ((int) last_time.tv_usec) - ((int) now_time.tv_usec) + 1000000 / PPS;
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) {
perror("gameLoop: select: stall");
exit(1);
}
}
}
gettimeofday(&last_time, NULL);
current_time = (time_t) last_time.tv_sec;
}
return;
}
void init_descriptor(int control) {
DESC *dnew = new DESC;
struct sockaddr_in sock;
struct hostent *from;
int desc, size;
size = sizeof(sock);
getsockname(control, (struct sockaddr *) &sock, (socklen_t *) &size);
if((desc = accept(control, (struct sockaddr *) &sock, (socklen_t *)&size)) < 0) {
perror("New_descriptor: accept");
return;
}
#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif
if(fcntl(desc, F_SETFL, FNDELAY) == -1) {
perror("New_descriptor: fcntl: FNDELAY");
return;
}
dnew->desc = desc;
dnew->connected = CON_GET_NAME;
size = sizeof(sock);
if(getpeername(desc, (struct sockaddr *) &sock, (socklen_t *) &size) < 0) {
perror("New_descriptor: getpeername");
dnew->host = "(unknown)";
}
else {
int addr = ntohl(sock.sin_addr.s_addr);
ostrstream ost;
ost << ((addr >> 24) & 0xFF) << '.' << ((addr >> 16) & 0xFF) << '.' << ((addr >> 8) & 0xFF) << '.' << ((addr) & 0xFF) << ends;
from = gethostbyaddr((char *) &sock.sin_addr, sizeof(sock.sin_addr), AF_INET);
dnew->host = (from ? from->h_name : ost.str());
wiz_echo(dnew->host + " has connected.\n\r");
log_string("Sock.sinaddr: " + dnew->host + " (" + ost.str() + ")");
}
dnew->printf(login_screen);
dnew->printf("Name: ");
return;
}
void save_players() {
log_string("Saving players...");
for(CH *ch = ch_list; ch; ch = ch->next)
ch->save();
return;
}
void tell_all(int sig) {
switch(sig) {
case SIGBUS:
log_string("SIGBUS (Bus Error");
break;
case SIGTERM:
log_string("SIGTERM (Term signal)");
break;
case SIGSEGV:
log_string("SIGSEG (Invalid memory Reference)");
break;
case SIGILL:
log_string("SIGILL (Illegal instruction)");
break;
case SIGFPE:
log_string("SIGFPE (Floating Point Error)");
break;
case SIGHUP:
log_string("SIGHUP (Hangup Signal)");
break;
default:
log_string("Unhandled Signal.");
break;
}
signal(sig, SIG_DFL);
log_string("Last command: " + lcom);
log_string("ERROR: Game has crashed!");
// do_copyover(NULL, "auto");
// save_players();
return;
}
void init_signals() {
signal(SIGBUS, tell_all);
signal(SIGTERM, tell_all);
signal(SIGFPE, tell_all);
signal(SIGILL, tell_all);
signal(SIGSEGV, tell_all);
signal(SIGHUP, tell_all);
signal(SIGSTOP, tell_all);
signal(SIGKILL, tell_all);
signal(6, tell_all);
return;
}
void bug(const string str) {
string str_bug = "[*****] BUG: " + str;
log_string(str_bug);
return;
}