#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/player/accept.c,v 2.8 1995/10/22 20:57:20 empire Exp $";
#endif

/*
 * player.c
 *
 * Keep track of people logged in to the game
 *
 * Dave Pare, 1994
 */

#include "misc.h"
#include "bit.h"
#include "proto.h"
#include "player.h"
#include "file.h"
#include "io_mask.h"
#include "lwp.h"
#include "io.h"
#include "power.h"

#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <fcntl.h>

void	player_login();

struct	qelem Players;

player_init()
{
	initque(&Players);
	init_player_commands();
}

struct player *
player_new(s, sin)
	int	s;
	struct	sockaddr_in *sin;
{
	extern char *inet_ntoa();
	struct	player *lp;
	struct hostent *hostp;

	lp = (struct player *) calloc(1, sizeof(struct player));
	bzero(lp, sizeof(struct player));
	if (sin) {
		/* update uses dummy player */
		insque(&lp->queue, &Players);
		strcpy(lp->hostaddr, inet_ntoa(sin->sin_addr));
#ifdef RESOLVE_IPADDRESS
		if (hostp = gethostbyaddr(&sin->sin_addr, sizeof(sin->sin_addr), AF_INET))
			strcpy(lp->hostname, hostp->h_name);
#endif /* RESOLVE_IPADDRESS */
		lp->cnum = 255;
		lp->bol = 1;
		time(&lp->curup);
		lp->iop = io_open(s, IO_READ|IO_WRITE|IO_NBLOCK,
			IO_BUFSIZE, 0, 0);
	}
	return lp;
}

struct player *
player_delete(lp)
	struct	player *lp;
{
	struct	player *back;

	back = (struct player *) lp->queue.q_back;
	if (back)
		remque(&lp->queue);
	if (lp->iop) {
		/* it's a real player */
		io_close(lp->iop);
		lp->iop = 0;
	}
	free((s_char *)lp);
	/* XXX may need to free bigmap here */
	return back;
}

struct player *
player_next(lp)
	struct	player *lp;
{
	if (lp == 0)
		lp = (struct player *)Players.q_forw;
	else
		lp = (struct player *)lp->queue.q_forw;
	if (&lp->queue == &Players)
		return 0;
	return lp;
}

struct player *
player_prev(lp)
	struct	player *lp;
{
	if (lp == 0)
		lp = (struct player *)Players.q_back;
	else
		lp = (struct player *)lp->queue.q_back;
	if (&lp->queue == &Players)
		return 0;
	return lp;
}

struct player *
getplayer(cnum)
	natid	cnum;
{
	register struct qelem *qp;

	for (qp = Players.q_forw; qp != &Players; qp = qp->q_forw)
		if (((struct player *)qp)->cnum == cnum)
			return (struct player *)qp;

	return 0;
}

struct player *
player_find_other(us, cnum)
	struct	player *us;
	register natid cnum;
{
	register struct qelem *qp;

	for (qp = Players.q_forw; qp != &Players; qp = qp->q_forw)
		if (((struct player *)qp)->cnum == cnum &&
		    (struct player *)qp != us)
			return (struct player *)qp;

	
	return 0;
}

int
player_wakeup_all(cnum)
	natid	cnum;
{
	register struct player *lp;

	if (lp = getplayer(cnum))
		return player_wakeup(lp);
}

player_wakeup(pl)
	struct	player *pl;
{
	if (pl->waiting)
		lwpWakeupFd(pl->proc);
}

/*ARGSUSED*/
void
player_accept(argc, argv)
	int	argc;
	char	**argv;
{
	extern	s_char *loginport;
	extern	int errno;
	struct	sockaddr_in sin;
	struct	servent *sp;
	int	s;
	short	port;
	int	val;
	int	maxfd;
	struct	player *np;
	int	len;
	int	ns;
	int	set;
	int	stacksize;
	char	buf[128];

	player_init();
	sp = getservbyname("empire", "tcp");
	if (sp == 0)
		port = htons(atoi(loginport));
	else
		port = sp->s_port;
	sin.sin_addr.s_addr = INADDR_ANY;
	sin.sin_port = port;
	sin.sin_family = AF_INET;
	if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		logerror("inet socket create");
		exit(1);
	}
	val = 1;
	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
		logerror("inet socket setsockopt SO_REUSEADDR");
		exit(1);
	}
	if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
		logerror("inet socket bind");
		exit(1);
	}
	if (listen(s, SOMAXCONN) < 0) {
		logerror("inet socket listen");
		exit(1);
	}
	maxfd = getdtablesize() - 1;
	while (1) {
		lwpSleepFd(s, LWP_FD_READ);
		len = sizeof(sin);
		ns = accept(s, (struct sockaddr *) &sin, &len);
		if (ns < 0) {
			logerror("new socket accept");
			continue;
		}
		(void) setsockopt(ns, SOL_SOCKET, SO_KEEPALIVE,
			&set, sizeof(set));
		if (ns >= maxfd) {
			logerror("new fd %d, max %d, no fd's left for new user",
				ns, maxfd);
			close(ns);
			continue;
		}
		np = player_new(ns, &sin);
		/* XXX may not be big enough */
		stacksize = 100000
/* budget */	   	  + max(WORLD_X*WORLD_Y/2 * sizeof(int) * 7,
/* power */			MAXNOC * sizeof(struct powstr));
		sprintf(buf, "player (fd #%d)", ns);
		lwpCreate(PP_PLAYER, player_login, stacksize,
			LwpCurrent->flags, buf, "Empire player", 0, 0, np);
	}
}
