/* hub [host [port [local-port]]]

   A simple multiplexer for Empire.  The first connecting client
   determines the country name and password.  Further connections must
   use the same name and password.

   -harmless@empire.net */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/errno.h>
/* #include <sys/select.h> */
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <netdb.h>
#include <fcntl.h>
#include <time.h>

/* some defaults in case nothing is specified.  Change as you wish. */
#define DEFPORT "6667"
#define DEFHOST "149.84.128.9"
#define LOCALPORT "6667"



extern void delete_player(int p);

/* flags for status fields */
#define COUNTRY_OK	0x0001
#define PASSWORD_OK	0x0002
#define PLAYING		0x0004
#define USER_OK		0x0008
#define CLIENT_OK	0x0010

struct stats {
	int lines_in,lines_out;
	int bytes_in,bytes_out;
	time_t start,latency;
};

struct country {
	int fd;		/* connection to server or upstream hub */
	FILE *fp;
	int length;	/* length of buffer */
	int max;	/* allocated length of buffer */
	char *buffer;

	struct stats stats;

	int status;
	char *name;	/* used during login */
	char *pass;	/* gotten from first login */
	int players;	/* number of connected players */
	int *player;	/* array of players */
	int focus;	/* player number with input focus */
	char *prompt;	/* current prompt, NULL if queueing */
	int commands;
	char **command;	/* commands queued while in focus */
	int minutes, btus;
};

struct player {
	int fd;		/* socket to player */
	FILE *fp;
	int length;	/* length of buffer */
	int max;	/* allocated length of buffer */
	char *buffer;

	struct stats stats;

	int status;	/* coun, pass, ok, ... */
	int coun;	/* country number in table */
	char *user;	/* stored for logging purposes */
	char *client;
};

/* common part of both structures, input buffer and file descriptor */
struct common {
	int fd;		/* socket to player */
	FILE *fp;	/* buffered output stream (same as fd) */
	int length;	/* length of buffer */
	int max;	/* allocated length of buffer */
	char *buffer;

	struct stats stats;
};


/* convert an internet sockaddr into a hostname or ip number */
char *addrname(struct sockaddr_in a) {
  struct hostent *he;
  he = gethostbyaddr((char *)&a.sin_addr, sizeof(a.sin_addr), AF_INET);
  if (he) return he->h_name; else return (inet_ntoa(a.sin_addr));
}



int countries=0,players=0;
struct country *country=NULL;
struct player *player=NULL;
struct sockaddr_in sock_in;		/* server machine/port */

int mirror=0;
int debug=0;

/* return a staticly allocated whitespace bounded string */
/* definitely not thread-safe */
char *strip_white(char *line) {
	int start,end,i;
	static char *str=NULL;
	static int length=0;
			
	for (start=0;line[start] && isspace(line[start]);start++) ;
	if (!line[start]) return NULL;

	for (i=start;line[i];i++) {
		if (!isspace(line[i])) end=i;
	}

	if (end-start+2 > length) {
		if (str) free(str);
		length = end-start+2;
		str=malloc(length);
	}
	strncpy(str,line+start,end-start+1);
	str[end-start+1]=0;
	return str;
}

char *player_host(int p) {
	struct sockaddr_in a;
	int i;
	i=sizeof(a);
	
	if (getpeername(player[p].fd, (struct sockaddr *)&a, &i) < 0) {
		perror("getpeername:");
		return "getpeername.error";
	}
	return addrname(a);
}


int count_newlines(char *s,int len) {
	int i;
	int count=0;
	for (i=0;i<len;i++) {
		if (s[i]=='\n') count++;
	}
	return count;
}

int recv_common(struct common *com) {
	int len;

	if (debug) printf("recv_common(%d) = ",com->fd);
	
	if (!com->buffer) {
		com->max=1024;
		com->buffer=malloc(com->max);
	}
	
	if (com->length == com->max) {
		com->max *=2;
		com->buffer = realloc(com->buffer,com->max);
	}
	
	len=recv(com->fd,com->buffer+com->length,com->max-com->length,0);
	
	com->stats.bytes_in += len;
	com->stats.lines_in += count_newlines(com->buffer+com->length,len);
	if (len>=0 && !com->stats.start) com->stats.start = time(NULL);

	if (debug) {
		printf("%d \"",len);
		if (len>0) fwrite(com->buffer+com->length,2,len,stdout);
		printf("\"\n");
	}
	
	if (len>0) com->length += len;
	if (len<0) perror("recv");
	
	
	return len;
}

char *common_getline(struct common *com) {
	char *end,*line;
	int len;

	if (debug) printf("getline(%d) = ",com->fd);
		
	end=memchr(com->buffer,'\n',com->length);
	if (!end) { if (debug) printf("\n"); return NULL; }

	len = end - com->buffer + 1;
	
	line=malloc(len+1);
	memcpy(line,com->buffer,len);	
	line[len] = '\0';

	com->length -= len;
	memmove(com->buffer, end+1, com->length);

	if (debug) printf("%s",line);
	
	com->stats.bytes_out += len;
	com->stats.lines_out++;
	if (!com->length) {
		com->stats.latency += time(NULL)-com->stats.start;
		com->stats.start=0;
	}	
	return line;
}

void print_stats(struct stats stats) {
	printf(" in: %d/%d  out: %d/%d  latency: %4.2lf secs\n",
		stats.bytes_in,stats.lines_in,
		stats.bytes_out,stats.lines_out,
		stats.latency / (double)stats.lines_out);
}

void delete_common(struct common *com) {
	if (debug) printf("delete_common(%d)\n",com->fd);
	fclose(com->fp);
	close(com->fd);
	if (com->buffer) free(com->buffer);
	com->buffer=NULL;
	print_stats(com->stats);
}

void delete_country(int c) {
	int i;

	if (debug) printf("delete_country(%d)\n",c);
	
	delete_common(&country[c]);

	if (country[c].name) free(country[c].name);
	if (country[c].pass) free(country[c].pass);
	if (country[c].prompt) free(country[c].prompt);

	for (i=0;i<country[c].commands;i++) {
		free(country[c].command[i]);
	}
	if (country[c].command) free(country[c].command);

	for (i=0;i<country[c].players;i++) {
		fprintf(player[country[c].player[i]].fp,
			"3 Server-Hub link lost\r\n");
		player[country[c].player[i]].coun = -1;
		delete_player(country[c].player[i]);
	}
	if (country[c].player) free(country[c].player);
	
	countries--;	
	memmove(&country[c],&country[countries],sizeof(struct country));
}

void copy_player(int src,int dst) {
	int i,j;
	memmove(&player[dst],&player[src],sizeof(struct player));
	for (i=0;i<countries;i++) {
		for (j=0;j<country[i].players;j++) {
			if (country[i].player[j] == src) {
				country[i].player[j] = dst;
			}
		}
	}
}
		
void delete_player(int p) {
	int i,c,p2;
	time_t t;


	if (debug) printf("delete_player(%d)\n",p);
	
	c=player[p].coun;
	if (c>=0) for (i=0;i<country[c].players;i++) {
		p2 = country[c].player[i];
		if (p2 != p) {
			fprintf(player[p2].fp,"d %s@%s (%s) logging out\r\n",
				player[p].user ? player[p].user : "nobody",
				player_host(p),
				player[p].client ? player[p].client : "");
		}
	}
	
	time(&t);
	printf("%s %s@%s (%s) logging out\n",
		strip_white(ctime(&t)),
		player[p].user ? player[p].user : "nobody",
		player_host(p),
		player[p].client ? player[p].client : "");


	
	delete_common(&player[p]);

	if (player[p].user) free(player[p].user);
	if (player[p].client) free(player[p].client);

	if (c>=0) {
		if (country[c].focus == p) {
			country[c].focus = -1;
			if (debug) printf("player with focus deleted\n");
		}			
		for (i=0;i<country[c].players;i++) {
			if (country[c].player[i] == p) {
				country[c].player[i] =
				country[c].player[country[c].players-1];
				break;
			}
		}
		country[c].players--;	
		if (!country[c].players) delete_country(c);
	}
	

	copy_player(players-1,p);
	players--;

}

/* strip off first whitespace bounded word */
/* start at next word, continuing to last graphical character */
/* if string begins and ends with double quotes, strip those too */
char *strip_args(char *line) {
	char *args;
	int i,first,last;
	
	for (first=0;line[first] && isspace(line[first]);first++) ;
	for (;line[first] && !isspace(line[first]);first++) ;
	for (;line[first] && isspace(line[first]);first++) ;
	for (i=last=first;line[i];i++) {
		if (!isspace(line[i])) last=i;
	}

	if (!line[first] || !line[last]) return NULL;
	
	if (line[first]=='"' && line[last]=='"') {
		first++;
		last--;
	}

	if (last<first-1) return NULL;
	
	args=malloc(last-first+2);
	strncpy(args,line+first,last-first+1);
	args[last-first+1] = 0;
	return args;
}

/* return a staticly allocated first whitespace bounded word */
char *strip_command(char *line) {
	int start,end;
	static char *command=NULL;
	static int length=0;
			
	for (start=0;line[start] && isspace(line[start]);start++) ;
	if (!line[start]) return NULL;
	
	for (end=start+1;line[end] && !isspace(line[end]);end++) ;

	if (end-start+1 > length) {
		if (command) free(command);
		length = end-start+1;
		command=malloc(length);
	}
	strncpy(command,line+start,end-start);
	command[end-start]=0;
	return command;
}


void new_country(int p, char *name) {
	int i,c,s;
	FILE *fp,*fc;
	fp=player[p].fp;
	
	
	s = socket(AF_INET, SOCK_STREAM, 0);

	if (debug) printf("new_country(%d,%s) = %d\n",p,name,s);

	if (s < 0) {
		perror("socket");
		fprintf(fp,"a Unable to create socket\r\n");
		return;
	}

	if (setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&i,sizeof(i))) {
		perror("unable to reuse socket");
	}

	if (connect(s, (struct sockaddr *)&sock_in, sizeof(sock_in)) < 0) {
		perror("connect");
		fprintf(fp,"a connect failure at hub\r\n");
		close(s);
		return;
	}

	fcntl(s,F_SETFL,O_NONBLOCK);

	fc = fdopen(s,"w");
	if (!fc) {
		perror("fdopen");
		fprintf(fp,"a fdopen() failure at hub\r\n");
		close(s);
		return;
	}
	if (debug) setlinebuf(fc);
	

	if (player[p].user) {
		fprintf(fc,"user %s\r\n",player[p].user);
	}
	
	if (player[p].client) {
		fprintf(fc,"client %s\r\n",player[p].client);
	}
	
	fprintf(fc,"coun \"%s\"\r\n",name);
	
	if (!countries) {
		if (!country) country=malloc(sizeof(*country));
		countries=1;
		c=0;
	} else {
		c = countries++;
		country = realloc(country,sizeof(*country)*countries);
	}
	
	player[p].coun = c;
	country[c].fd = s;
	country[c].fp = fc;
	country[c].length=0;
	country[c].max=1024;
	country[c].buffer=malloc(country[c].max);
	
	country[c].status=0;
	country[c].name = name;
	country[c].pass = NULL;
	country[c].players=1;
	country[c].player = malloc(sizeof(*country[c].player) * 10);
	country[c].player[0] = p;	
	country[c].focus = p;
	country[c].prompt = NULL;
	country[c].commands = 0;
	country[c].command = NULL;

	fflush(fc);
}

	
void login_coun(int p,char *name) {
	int i;

	if (debug) printf("login_coun(%d,%s)\n",p,name);
	
	player[p].status &= ~PASSWORD_OK;


	if (debug) {
		time_t t;
		printf("%s %s@%s (%s) connecting to %s\n",
			strip_white(ctime(&t)),
			player[p].user ? player[p].user : "nobody",
			player_host(p),
			player[p].client ? player[p].client : "", name);
	}
	
	for (i=0;i<countries && strcmp(name,country[i].name);i++) ;

	if (i<countries) {
		fprintf(player[p].fp,
			"0 country name %s\r\n",name);
		player[p].coun = i;
		player[p].status |= COUNTRY_OK;
		country[i].players++;
		country[i].player = realloc(country[i].player,
			country[i].players * sizeof(*country[i].player));
		country[i].player[country[i].players-1] = p;
		free(name);
		return;
	
	} else {
		new_country(p,name);
	}
	
	
}

void login_pass(int p,char *pass) {
	int i,c;
	FILE *fp,*fc;

	if (debug) printf("login_pass(%d,%s)\n",p,pass);
	
	fp = player[p].fp;	
	c=player[p].coun;

	if (c<0) {
		fprintf(fp,"a need country first\r\n");
		return;
	}
			

	{
		time_t t;
		time(&t);
		printf("%s %s@%s (%s) password for %s",
			strip_white(ctime(&t)),
			player[p].user ? player[p].user : "nobody",
			player_host(p),
			player[p].client ? player[p].client : "",
			country[c].name);
	}

	if (country[c].status & PASSWORD_OK) {
		if (strcmp(country[c].pass,pass)) {
			fprintf(fp,"a bad password, logging entry\r\n");
			printf(" failed (%s)\n",pass);
			return;
		}
		printf(" ok\n");
		fprintf(fp,"0 password ok\r\n");
		return;
	}

	printf(" forwarded to server\n");
	
	fc=country[c].fp;
	
	fprintf(fc,"pass \"%s\"\r\n",pass);
	if (debug) printf("server: pass \"%s\"\r\n",pass);

	if (country[c].pass) free(country[c].pass);
	country[c].pass = pass;

	return;
}

	
void login_play(int p) {
	int c,i,p2;
	FILE *fp;

	if (debug) printf("login_play(%d)\n",p);
	
	c = player[p].coun;
	fp = player[p].fp;
	
	if (c<0) {
		fprintf(fp,"a need country and password\r\n");
		return;
	}

	player[p].status |= PLAYING;

	if (!(country[c].status & PLAYING)) {		
		fprintf(country[c].fp,"play\r\n");
		return;
	} else {
		if (debug) printf("another client already logged in\n");
	}
	
	{
		time_t t;
		time(&t);
		printf("%s %s@%s (%s) using country %s\n",
			strip_white(ctime(&t)),
			player[p].user ? player[p].user : "nobody",
			player_host(p),
			player[p].client ? player[p].client : "",
			country[c].name);
	}
			

	fprintf(fp,"2 2\r\n1 EmpireHub 1.0.1\r\n");

	/* send a flash message to all other connected players and
	   write a login message to the new player listing all the
	   current connections.  */
	for (i=0;i<country[c].players;i++) if (country[c].player[i] != p) {
		p2 = country[c].player[i];
		fprintf(player[p2].fp,"d %s@%s (%s) logging in\r\n",
			player[p].user ? player[p].user : "nobody",
			player_host(p),
			player[p].client ? player[p].client : "");

		fprintf(fp,"1    %s@%s (%s) connected\r\n",
			player[p2].user ? player[p2].user : "nobody",
			player_host(p2),
			player[p2].client ? player[p2].client : ""); 
	}

	fprintf(fp,"6 %d %d\r\n",country[c].minutes,country[c].btus);

	return;
}

void login(int p,char *line) {
	FILE *fp;
	char *command;

	if (debug) printf("login(%d) %s",p,line);
	
	fp = player[p].fp;
	command=strip_command(line);

	if (!command) fprintf(fp,"b Command  not found\r\n");
	
	if (!strcmp(command,"user")) {
		if (player[p].user) free(player[p].user);
		player[p].user = strip_args(line);
		if (player[p].user) {
			fprintf(fp,"0 hello %s\r\n",player[p].user);
		} else {
			fprintf(fp,"b Usage user username\r\n");
		}
		
	} else if (!strcmp(command,"client")) {
		if (player[p].client) free(player[p].client);
		player[p].client = strip_args(line);
		if (player[p].client) {
			fprintf(fp,"0 talking to %s\r\n",player[p].client);
		} else {
			fprintf(fp,"b Usage client clientname [version info]\r\n");
		}

	} else if (!strcmp(command,"coun")) {
		login_coun(p,strip_args(line));

	} else if (!strcmp(command,"pass")) {
		login_pass(p,strip_args(line));

	} else if (!strcmp(command,"play")) {
		login_play(p);
	} else {
		fprintf(fp,"b Command %s not found\r\n",command);
	}
	
	free(line);
}

void add_command(int c,char *line) {

	if (debug) printf("add_command(%d) %s",c,line);

	if (!country[c].command) {
		country[c].command = malloc(10*sizeof(char *));
	} else {
		country[c].command = realloc(country[c].command,
			(country[c].commands+1) * sizeof(char *));
	}
	
	country[c].command[country[c].commands++] = line;
}

char *remove_command(int c) {
	char *line;
	int i;
	
	if (debug) printf("remove_command(%d)\n",c);
	
	if (!country[c].commands) return NULL;

	line = country[c].command[0];
		
	for (i=1;i<country[c].commands;i++)
		country[c].command[i-1] = country[c].command[i];

	country[c].commands--;
	
	return line;
}

void play_prompt(int p, int c, char *line) {
	int i;

	if (debug) printf("play_prompt(%d,%d) %s",p,c,line);

	fprintf(country[c].fp,"%s",line);

	if (mirror) {
		for (i=0;i<country[c].players;i++) {
			if (country[c].player[i] != p) {
				fprintf(player[country[c].player[i]].fp,
					"g %s",line);
			}
		}
	}
	country[c].focus = p;
	free(line);
}

void play_subprompt(int c, char *line) {
	int i;

	if (debug) printf("play_subprompt(%d) %s",c,line);
	
	fprintf(country[c].fp,"%s",line);

	if (mirror) {
		for (i=0;i<country[c].players;i++) {
			if (country[c].player[i] != country[c].focus) {
				fprintf(player[country[c].player[i]].fp,
					"h %s %s",
					country[c].prompt,
					line);
			}
		}
	}
	free(line);
	free(country[c].prompt);
	country[c].prompt = NULL;
}

void play_focus(int c, char *line) {

	if (debug) printf("play_focus(%d,%s)\n",c,line);
	
	fprintf(country[c].fp,"%s",line);
	add_command(c,line);
}
					
						
void play(int p,char *line) {
	int c;
	
	if (debug) printf("play(%d) %s",p,line);
	
	c = player[p].coun;

	if (country[c].focus<0) play_prompt(p,c,line);
	else {
		if (p != country[c].focus) {
			if (debug) printf("focus error: early read\n");
		}
		if (country[c].prompt) play_subprompt(c,line);
		else play_focus(c,line);
	}
}

	
	
void recv_player(int p) {
	int i;
	char *line;
	
	if (debug) printf("recv_player(%d)\n",p);
	
	i=recv_common(&player[p]);
	
	if ( i <= 0 ) {
		delete_player(p);
		return;
	} else {
		if (debug) printf(" %d characters - \n",i);
	}
	
	while (line = common_getline(&player[p])) {
		if (player[p].status & PLAYING) {
			play(p,line);	/* pays attention to focus */
		} else {
			login(p,line);	/* no focus */
		}
	}
}


void parse_play(int c,char *line) {
	int i,focus;
	FILE *fp = NULL;
	char *command;

	if (debug) printf("parse_play(%d) %s",c,line);
	
	focus = country[c].focus;
	if (focus>=0) fp=player[focus].fp;
	
	switch (*line) {

			/* send to client with focus */
			/* mirror to others (if enabled) */
	case '1':
		if (fp) fprintf(fp,"%s",line);
		else if (debug) printf("focus: %s",line);
		if (mirror) {
			for (i=0;i<country[c].players;i++) {
				if (country[c].player[i] != focus) {
					fprintf(player[country[c].player[i]].fp,"i%s",line+1);
				}
			}
		}
		break;


			/* all of these go to all clients */
	case '3':
	case 'd':
	case 'e':
	case 'f':
		if (debug) printf("%s",line);
		for (i=0;i<country[c].players;i++) {
			fprintf(player[country[c].player[i]].fp,"%s",line);
		}		
		break;
		
				
	case '4':
	case '5':
		if (!fp) {
			if (debug) printf("focus: %s",line);
			break;
		}

		fprintf(fp,"%s",line);
		
		command = remove_command(c);

		if (command && mirror) {
			for (i=0;i<country[c].players;i++) {
				if (country[c].player[i] != focus) {
					fprintf(player[country[c].player[i]].fp,
						"h %s %s",
						strip_white(line+1),
						command);
				}
			}
		}

		if (!command) {
			country[c].prompt = strdup(strip_white(line+1));
			free(line);
		}
									
		if (command) free(command);
		
		break;

		
	case '6':
		sscanf(line+1,"%d%d",&country[c].minutes,&country[c].btus);

		if (!fp) {
			if (debug) printf("focus: %s",line);
			break;
		}

		if (country[c].commands == 0) country[c].focus = -1;

		fprintf(fp,"%s",line);		
		command = remove_command(c);

		if (command && mirror) {
			for (i=0;i<country[c].players;i++) {
				if (country[c].player[i] != focus) {
					fprintf(player[country[c].player[i]].fp,
						"g %d %d %s",
						country[c].minutes,
						country[c].btus,
						command);
				}
			}
		}
			
		if (command) free(command);
		
						
		break;
	
	case '8':
	case '9':
		if (fp) fprintf(fp,"%s",line);
		else if (debug) printf("focus: %s\n",line);
		break;
		
	default:
		if (debug) printf("default: %s",line);
		break;
	}
}	
		

void parse_login(int c,char *line) {
	int focus;
	FILE *fp = NULL;

	if (debug) printf("parse_login(%d) %s",c,line);
	
	focus = country[c].focus;
	if (focus>=0) fp=player[focus].fp;
	
	switch (*line) {
	case '2':
		if (!strncmp(line,"2 2",3)) {
			if (debug) printf("switching to play mode\n");
			country[c].status |= PLAYING;
			if (fp) fprintf(fp,"%s",line);
			else if (debug) printf("focus error: %s",line);
		}
		break;
				
	case '0':

		if (!strncmp(line,"0 country name",14)) {
			if (debug) printf("country name ok\n");
			if (fp) fprintf(fp,"%s",line);
			else if (debug) printf("focus error: %s",line);
			country[c].status |= COUNTRY_OK;
			if (focus>=0) player[focus].status |= COUNTRY_OK;
			
		} else if (!strncmp(line,"0 password ok",13)) {
			time_t t;
			time(&t);
			if (debug) printf("password is ok\n");
			if (fp) fprintf(fp,"%s",line);
			else if (debug) printf("focus error: %s",line);
			country[c].status |= PASSWORD_OK;
			if (focus>=0) player[focus].status |= PASSWORD_OK;
			printf("%s %s@%s (%s) password for %s ok\n",
				strip_white(ctime(&t)),
				player[focus].user ? player[focus].user : "nobody",
				player_host(focus),
				player[focus].client ? player[focus].client : "",
				country[c].name);
		} else {
			printf("unknown reply %s",line);
		}
		
		break;
		
	case 'a':
	case 'b':
	case '3':
		if (fp) fprintf(fp,"%s",line);
		else if (debug) printf("focus error: %s",line);
		break;
			 
		
	default:
		if (debug) printf("mode error: %s",line);
	}
	free(line);
}
	
void recv_country(int c) {
	int i;
	char *line;
	
	if (debug) printf("recv_country(%d)\n",c);
	
	i=recv_common(&country[c]);
	if (i<=0) {
		delete_country(c);
	}

	while (line = common_getline(&country[c])) {
		if (country[c].status & PLAYING) parse_play(c,line);
		else parse_login(c,line);
	}
}

void new_player(int listen_fd) {
	int i,p;
	struct sockaddr_in addr;

	if (debug) printf("new_player()\n");
	
	p=players;
	players++;
	
	if (!player) {
		player=malloc(sizeof(*player));
	} else {
		player = realloc(player,players*sizeof(*player));
	}
	
	i=sizeof(addr);
	player[p].fd = accept(listen_fd,(struct sockaddr *)&addr,&i);
	if (player[p].fd < 0) {
		perror("accept");
		players--;
		return;
	}

	if (debug) printf("accept() from %s\n",addrname(addr));

	player[p].fp = fdopen(player[p].fd,"w");
	if (!player[p].fp) {
		perror("fdopen");
		close(player[p].fd);
		players--;
		return;
	}
	if (debug) setlinebuf(player[p].fp);

	player[p].length=0;
	player[p].max=1024;
	player[p].buffer=malloc(player[p].max);
	player[p].status=0;
	player[p].coun = -1;
	player[p].user = NULL;
	player[p].client = NULL;
	
	fprintf(player[p].fp,"2 Empire server ready\r\n");
}
		
	
	
	
/* return number of players connected */
int main_loop(int listen_fd) {
	int i;
	fd_set in;
	
	if (debug) printf("waiting on select: ");

	FD_ZERO(&in);
	FD_SET(listen_fd,&in);
	for (i=0;i<countries;i++) {
		FD_SET(country[i].fd,&in);
		if (debug) printf(" %d",country[i].fd);
	}
	for (i=0;i<players;i++) {
		if (player[i].coun<0 ||
		    ((country[player[i].coun].focus == i) ||
		      country[player[i].coun].focus < 0) ) {
			FD_SET(player[i].fd,&in);
			if (debug) printf(" %d",player[i].fd);
		} else {
			if (debug) printf(" !%d",player[i].fd);
		}
	}
	
	i=select(FD_SETSIZE,&in,NULL,NULL,NULL);
	if (debug) printf(" select done\n");

	if (i<0) {
		perror("select");
		return -1;
	}

	if (FD_ISSET(listen_fd,&in)) {
		new_player(listen_fd);
	}

	/* while loops in case recv_country/recv_player delete the
	 * the current country or player (due to network failure) */
	 	
	for (i=0;i<countries;i++) {
		while (FD_ISSET(country[i].fd,&in)) {
			FD_CLR(country[i].fd,&in);
			recv_country(i);
		}
	}
	
	for (i=0;i<players;i++) {
		while (FD_ISSET(player[i].fd,&in)) {
			FD_CLR(player[i].fd,&in);
			recv_player(i);
		}
	}

	for (i=0;i<countries;i++) fflush(country[i].fp);
	for (i=0;i<players;i++) fflush(player[i].fp);
	
	return players;
}

int open_listen(int port) {
	struct sockaddr_in addr;
	int fd;
	int i;
	
	fd=socket(PF_INET,SOCK_STREAM,getprotobyname("tcp")->p_proto);

	if (fd<0) {
		perror("unable to create socket");
		return -errno;
	}

	if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char *)&i,sizeof(i))) {
		perror("unable to reuse socket");
	}

	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(port);
	if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))) {
		perror("bind error");
		return -errno;
	}

	if (listen(fd,5)) {
		perror("listen failed");
		return -errno;
	}

	return fd;
}

int main(int argc, char **argv)
{
	int i;
	int persistent=0;		/* server sticks around even when no clients */
	int listen_fd=-1;
	int parameters=0;
	struct hostent *hp;
	char *host=DEFHOST;
	char *port=DEFPORT;
	char *localport=LOCALPORT;

	for (i=1;i<argc;i++) {
		if (argv[i][0]=='-') {
			if (!strcmp(argv[i],"-persistent")) {
	persistent=1;
			} else if (!strcmp(argv[i],"-debug")) {
	debug=1;
			} else if (!strcmp(argv[i],"-help")) {
	printf("hub [options] [host [remoteport [localport]]]\n\
Options: -help         this page\n\
         -persistent   hub remains alive after last client exits\n\
         -debug        hub prints lots of useless info while working\n\
         -mirror       mirror data from all other clients\n");
	exit(0);
      } else if (!strcmp(argv[i],"-mirror")) {
	mirror=1;
      } else {
	printf("unkown option: %s\n",argv[i]);
      }
    } else {
	switch (parameters) {
	case 0: host=argv[i]; break;
	case 1: port=argv[i]; break;
	case 2: localport=argv[i]; break;
	default: fprintf(stderr,"Unknown argument: %s\n",argv[i]);
	}
	parameters++;
    }
  }

	if (isdigit(*host)) {
		sock_in.sin_addr.s_addr = inet_addr(host);
	} else {
		hp = gethostbyname(host);
		if (!hp) {
			printf("%s: No such host\n", host);
			return -errno;
		}
		memcpy( &sock_in.sin_addr, hp->h_addr, sizeof(sock_in.sin_addr));
	}
	sock_in.sin_port = htons(atoi(port));
	sock_in.sin_family = AF_INET;


	listen_fd = open_listen(atoi(localport));

/*	signal(SIGHUP,sighup); */

	do {
		while (main_loop(listen_fd)>0) {}

	} while (persistent);

	close(listen_fd);
	return 0;
}



