+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996 Mark H. Weaver <mhw@netris.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: game.c,v 1.39 1999/05/16 06:56:27 mhw Exp $
+ */
+
+#define NOEXT
+#include "netris.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <setjmp.h>
+
+#define HEADER_SIZE sizeof(netint4[3])
+#define MAX_CONNECTIONS 3
+
+char *version_string = "0.5.89";
+
+static struct option options[] = {
+ { "wait", 0, 0, 'w' },
+ { "port", 1, 0, 'p' },
+ { "speed", 1, 0, 'i' },
+ { "seed", 1, 0, 's' },
+ { "info", 0, 0, 'H' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
+
+ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
+
+EventGenRec netGen[MAX_SCREENS] = {
+ { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE } };
+
+static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
+static EventGenRec alarmGen =
+ { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
+static EventGenRec *nextGen = &alarmGen;
+
+static sigjmp_buf close_env;
+
+ExtFunc volatile void die(char *msg)
+{
+ perror(msg);
+ exit(1);
+} //die
+
+ExtFunc int MyRead(int fd, void *data, int len)
+{
+ int result, left;
+
+ left = len;
+ while (left > 0) {
+ result = read(fd, data, left);
+ if (result > 0) {
+ data = ((char *)data) + result;
+ left -= result;
+ }
+ else if (errno != EINTR)
+ return result;
+ }
+ return len;
+} //MyRead
+
+ExtFunc int MyWrite(int fd, void *data, int len)
+{
+ int result, left;
+
+ left = len;
+ while (left > 0) {
+ result = write(fd, data, left);
+ if (result > 0) {
+ data = ((char *)data) + result;
+ left -= result;
+ }
+ else if (errno != EINTR)
+ return result;
+ }
+ return len;
+} //MyWrite
+
+static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
+{
+ return E_alarm;
+}
+
+ExtFunc void AddEventGen(EventGenRec *gen)
+{
+ assert(gen->next == NULL);
+ assert(nextGen->next != (void*)0xffffffff);
+ gen->next = nextGen->next;
+ nextGen->next = gen;
+} //AddEventGen
+
+ExtFunc void RemoveEventGen(EventGenRec *gen)
+{
+ // assert(gen->next != NULL); /* Be more forgiving, for SIGINTs */
+ if (gen->next) {
+ while (nextGen->next != gen)
+ nextGen = nextGen->next;
+ nextGen->next = gen->next;
+ gen->next = NULL;
+ }
+} //RemoveEventGen
+
+ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
+{ //poll
+ int i, retry = 0;
+ fd_set fds[FT_len];
+ EventGenRec *gen;
+ int result, anyReady, anySet;
+ struct timeval tv;
+
+ for (;;) {
+ for (i = 0; i < FT_len; ++i)
+ FD_ZERO(&fds[i]);
+ anyReady = anySet = 0;
+ gen = nextGen;
+ do {
+ if (gen->mask & mask) {
+ if (gen->ready)
+ anyReady = 1;
+ if (gen->fd >= 0) {
+ FD_SET(gen->fd, &fds[gen->fdType]);
+ anySet = 1;
+ }
+ }
+ gen = gen->next;
+ } while (gen != nextGen);
+ if (anySet) {
+ tv.tv_sec = 0;
+ tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
+ result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
+ &fds[FT_except], anyReady ? &tv : NULL);
+ }
+ else {
+ if (retry && !anyReady)
+ sleep(1);
+ result = 0;
+ }
+ gen = nextGen;
+ do {
+ if ((gen->mask & mask)
+ && (gen->ready || (result > 0 && gen->fd >= 0
+ && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
+ gen->ready = 0;
+ event->type = gen->func(gen, event);
+ if (event->type != E_none) {
+ nextGen = gen->next;
+ return event->type;
+ }
+ }
+ gen = gen->next;
+ } while (gen != nextGen);
+ retry = 1;
+ }
+} //WaitMyEvent
+
+ExtFunc void ByeClient(int playa)
+{ //client went away
+ fprintf(stderr, "Close connection #%d\n", playa);
+ close(netGen[playa].fd);
+ netGen[playa].fd = -1;
+} //ByeClient
+
+ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
+{ //receive
+ int result;
+ short uid, type, size;
+ netint4 *data = (netint4*)&(gen->buf);
+
+ result = MyRead(gen->fd, gen->buf + gen->bufSize,
+ gen->bufGoal - gen->bufSize);
+ if (result < 0) {
+ ByeClient(gen->player);
+ type = NP_endConn;
+ return E_net;
+ }
+ gen->bufSize += result;
+ if (gen->bufSize < gen->bufGoal)
+ return E_none;
+ // *ugly* memcpy(data, gen->buf, sizeof(data));
+ uid = ntoh4(data[0]);
+ type = ntoh4(data[1]);
+ size = ntoh4(data[2]);
+ gen->bufGoal = size;
+ if (gen->bufSize < gen->bufGoal)
+ return E_none;
+ gen->bufSize = 0;
+ gen->bufGoal = HEADER_SIZE;
+ event->u.net.sender = gen->player;
+ event->u.net.uid = uid;
+ event->u.net.type = type;
+ event->u.net.size = size - HEADER_SIZE;
+ event->u.net.data = gen->buf + HEADER_SIZE;
+ if (type == NP_endConn)
+ ByeClient(gen->player);
+ return E_net;
+} //NetGenFunc
+
+ExtFunc void SendPacketTo(short playa, short uid, NetPacketType type, int size, void *data)
+{ //send to someone
+ netint4 header[3];
+
+ if (netGen[playa].fd >= 0) {
+ header[0] = hton4(uid);
+ header[1] = hton4(type);
+ header[2] = hton4(size + HEADER_SIZE);
+ if (MyWrite(netGen[playa].fd, header, HEADER_SIZE) != HEADER_SIZE)
+ die("write (header)");
+ if (size > 0 && data && MyWrite(netGen[playa].fd, data, size) != size)
+ die("write");
+ }
+} //SendPacketTo
+
+
+ExtFunc void AtExit(void (*handler)(void))
+{
+#ifdef HAS_ON_EXIT
+ on_exit((void *)handler, NULL);
+#else
+ atexit(handler);
+#endif
+}
+
+ExtFunc void SCloseNet(short playa)
+{ //kick some connection's ass!
+ MyEvent event;
+
+ if (netGen[playa].fd >= 0) {
+ SendPacketTo(playa, 0, NP_endConn, 0, NULL);
+ do{} while (WaitMyEvent(&event, EM_net) != E_lostConn);
+ close(netGen[playa].fd);
+ netGen[playa].fd = -1;
+ }
+ if (netGen[playa].next)
+ RemoveEventGen(&netGen[playa]);
+} //SCloseNet
+
+ExtFunc void CloseNets(void)
+{ //nou oogjes dicht en snaveltjes toe
+ int i;
+
+ for (i = 1; i <= totalPlayers; i++)
+ SCloseNet(i);
+} //CloseNets
+
+ExtFunc int WaitForConnection(short port)
+{
+ struct sockaddr_in addr;
+ struct hostent *host;
+ int sockListen;
+ int addrLen;
+ int val1;
+ struct linger val2;
+ int i;
+
+ AtExit(CloseNets);
+ for (i = 1; i < MAX_SCREENS; i++)
+ memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if ((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ die("socket");
+ val1 = 1;
+ setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR,
+ (void *)&val1, sizeof(val1));
+ if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ die("bind");
+ if (listen(sockListen, 1) < 0)
+ die("listen");
+
+ addrLen = sizeof(addr);
+ for (i = 1; i <= MAX_CONNECTIONS; i++) {
+ if ((netGen[i].fd = accept(sockListen, (struct sockaddr *)&addr, &addrLen)) < 0)
+ die("accept");
+ fprintf(stderr, "Connection: %s\n", inet_ntoa(addr.sin_addr));
+ val2.l_onoff = 1;
+ val2.l_linger = 0;
+ setsockopt(netGen[i].fd, SOL_SOCKET, SO_LINGER, (void *)&val2, sizeof(val2));
+ sprintf(Players[i].host, "%s", inet_ntoa(addr.sin_addr));
+ if (addr.sin_family == AF_INET) {
+ host = gethostbyaddr((void *)&addr.sin_addr,
+ sizeof(struct in_addr), AF_INET);
+ if (host) {
+ strncpy(Players[i].host, host->h_name, sizeof(Players[i].host) - 1);
+ Players[i].host[sizeof(Players[i].host) - 1] = 0;
+ }
+ }
+ AddEventGen(&netGen[i]);
+ netGen[i].player = i;
+ totalPlayers++;
+ {
+ MyEvent event;
+
+ do {} while ((WaitMyEvent(&event, EM_net) != E_net) ||
+ (event.u.net.sender != i));
+ if (event.u.net.type != NP_hello) ByeClient(i);
+ else {
+ netint4 versiondata[2];
+ char data[255];
+ int major;
+ memcpy(versiondata, event.u.net.data, sizeof(versiondata));
+ major = ntoh4(versiondata[0]);
+ protocolVersion = ntoh4(versiondata[1]);
+ if (major != MAJOR_VERSION || protocolVersion != PROTOCOL_VERSION) {
+ snprintf(data, sizeof(data), "Version mismatch: received %d.%d",
+ major, protocolVersion);
+ fprintf(stderr, "Byebye client #%d (%s)\n",
+ event.u.net.sender, data);
+ SendPacketTo(event.u.net.sender, 0, NP_error, strlen(data)+1, data);
+ SCloseNet(event.u.net.sender);
+ } //version mismatch
+ fprintf(stderr, "Accepted #%d\n", event.u.net.sender);
+ } //NP_hello
+ }
+ }
+ close(sockListen);
+ return 0;
+} //WaitForConnection
+
+ExtFunc int StartServer(char *portStr)
+{
+ MyEvent event;
+ char serverdata[255];
+ int playercount;
+ int i;
+
+ {
+ short port;
+
+ if (portStr)
+ port = atoi(portStr); /* XXX Error checking */
+ else
+ port = DEFAULT_PORT;
+ WaitForConnection(port);
+ }
+
+ playercount = MAX_CONNECTIONS;
+
+ for (i = 1; i <= playercount; i++) {
+ sprintf(serverdata, "Netris server %s", version_string);
+ SendPacketTo(i, i, NP_hello, strlen(serverdata)+1, serverdata);
+ }
+
+ while(1) {
+ if (WaitMyEvent(&event, EM_net) == E_net) {
+// fprintf(stderr, "in %d: %d\n",
+// netGen[event.u.net.sender].fd, event.u.net.type);
+ switch(event.u.net.type) {
+ case NP_endConn:
+ { //client went away :(
+ //tell the others! :)
+ break;
+ } //NP_endConn
+ case NP_hello:
+ break;
+ case NP_newPlayer:
+ { //receive player details and return other players
+ memcpy(&Players[event.u.net.sender],
+ event.u.net.data, event.u.net.size);
+ fprintf(stderr, "player %d: %s <%s>\n", event.u.net.sender,
+ event.u.net.data, //Players[event.u.net.sender].name
+ Players[event.u.net.sender].host);
+ for (i = 1; i <= totalPlayers; i++)
+ if (i != event.u.net.sender)
+ SendPacketTo(i, event.u.net.sender, event.u.net.type,
+ sizeof(Player) - sizeof(Players[0].spy),
+ &Players[event.u.net.sender]);
+ if (--playercount == 0) {
+ fprintf(stderr, "Starting game\n");
+ for (i = 1; i <= totalPlayers; i++)
+ SendPacketTo(i, 0, NP_goAhead, 0, NULL);
+ playercount++;
+ } //give go ahead
+ break;
+ } //NP_playerdata
+ default:
+ { //relay data to all players
+ // if (event.u.net.type >= NP_pause)
+ for (i = 1; i <= totalPlayers; i++)
+ if (i != event.u.net.sender)
+ SendPacketTo(i, event.u.net.sender, event.u.net.type,
+ event.u.net.size, event.u.net.data);
+ break;
+ }
+ } //E_net
+ }
+ } //loop
+} //StartServer
+
+
+ExtFunc void Header(void)
+{
+ fprintf(stderr,
+ "NETRIS Server %s\t(c) 2002 Shiar <shiar@shiar.org>\n\n",
+ version_string);
+} //Header
+
+ExtFunc void Usage(void)
+{
+ Header();
+ fprintf(stderr,
+ "Usage: netris <options>\n"
+ "\n"
+ " -h, --help\t\tPrint this usage information\n"
+ " -H, --info\t\tShow distribution and warranty information\n"
+ "\n"
+ " -p, --port <port>\tSet port number (default is %d)\n"
+ "\n"
+ " -s, --seed <seed>\tStart with given random seed\n"
+ " -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
+ "\n", DEFAULT_PORT);
+}
+
+ExtFunc void DistInfo(void)
+{
+ Header();
+ fprintf(stderr,
+ "This program is free software; you can redistribute it and/or modify\n"
+ "it under the terms of the GNU General Public License as published by\n"
+ "the Free Software Foundation; either version 2 of the License, or\n"
+ "(at your option) any later version.\n"
+ "\n"
+ "This program is distributed in the hope that it will be useful,\n"
+ "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
+ "GNU General Public License for more details.\n"
+ "\n"
+ "You should have received a copy of the GNU General Public License\n"
+ "along with this program; if not, write to the Free Software\n"
+ "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
+ "\n");
+} //DistInfo
+
+ExtFunc void WriteConf(void)
+{
+ FILE *file_out;
+
+ file_out = fopen(CONFIG_FILE, "w");
+ if (file_out == NULL) {
+ perror("Error writing config file");
+ exit(1);
+ }
+
+ fprintf(file_out, "### NETRIS %s Config file ###\n\n", version_string);
+
+ fclose(file_out);
+ fprintf(stderr, "Wrote new game configuration to %s\n", CONFIG_FILE);
+} //WriteConf
+
+ExtFunc void HandleOption(char tag, char *value)
+{
+ switch (tag) {
+ case 'p': //port
+ portStr = value; break;
+ case 'i': //speed (of level 1)
+ Game.initspeed = atof(value) * 1e6;
+ break;
+ case 's': //seed
+ Game.seed = atoi(value);
+ Players[0].flags |= SCF_setSeed;
+ break;
+ case 'H': //info
+ DistInfo(); exit(0);
+ case 'h': //help
+ Usage(); exit(0);
+ default:
+ break;
+ }
+} //HandleParam
+
+ExtFunc void ReadConf(char *filename)
+{
+ FILE *file_in;
+ char buf[513];
+ int i;
+ char *ch;
+ char tag[81], value[81];
+
+ file_in = fopen(filename, "r");
+ if (file_in) {
+ while (fgets(buf, 512, file_in) != NULL) {
+ if ((ch = strchr(buf, '#')))
+ *ch = '\0'; // truncate string from # char
+ for (i = strlen(buf)-1; i >= 0; i--)
+ if (buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\t' || buf[i] == 13)
+ buf[i] = '\0';
+ else break;
+
+ sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
+ for (i = 0; options[i].name; i++){
+ if (!strcasecmp(options[i].name, tag)) {
+ HandleOption(options[i].val, value);
+ break;
+ }
+ }
+ }
+ fclose(file_in);
+ } //read file
+ else {
+ fprintf(stderr, "Unable to open config file %s.\n", filename);
+ } //defaults
+
+} //ReadConf
+
+ExtFunc void CatchInt(int sig)
+{
+ siglongjmp(close_env, 1);
+}
+
+ExtFunc int main(int argc, char **argv)
+{
+ char ch;
+
+ if (sigsetjmp(close_env, 1)) exit(0);
+ signal(SIGINT, CatchInt);
+ Game.standout = Game.color = 1;
+ Game.initspeed = DEFAULT_INTERVAL;
+
+// if (getopt(argc, argv, "f:") == 'f')
+// ReadConf(optarg);
+// else
+ ReadConf(CONFIG_FILE);
+ while ((ch = getopt_long(argc, argv,
+ "hHp:i:s:", options, NULL)) != -1)
+ HandleOption(ch, optarg);
+ if (optind < argc) {
+ Usage();
+ exit(1);
+ }
+// WriteConf();
+
+ Header();
+ StartServer(portStr);
+ return 0;
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */