X-Git-Url: http://git.shiar.net/netris.git/blobdiff_plain/21add7c13bc1df386e45aad2939ee5ff2a152c2b..4f561019fc85c2817e3a72341397d1df32bc0868:/server.c diff --git a/server.c b/server.c index 9297b13..f67721f 100644 --- a/server.c +++ b/server.c @@ -37,30 +37,42 @@ static struct option options[] = { { "wait", 0, 0, 'w' }, { "port", 1, 0, 'p' }, - { "connections",1, 0, 'c' }, + { "min-players",1, 0, 'm' }, + { "max-players",1, 0, 'x' }, + { "continuous", 1, 0, 'c' }, { "speed", 1, 0, 'i' }, { "seed", 1, 0, 's' }, + { "verbose", 0, 0, 'v' }, { "info", 0, 0, 'H' }, { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; -static char Connections = 2; +static char minplayers = 2; +static char countPlayers = 0; +static char verbose = 0; + +struct sockaddr_in addr; static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" }; ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event); - -EventGenRec netGen[MAX_SCREENS] = { +static 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 }; + { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm }; + +static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event); +static EventGenRec connGen = + { NULL, 0, FT_read, -1, ConnGenFunc, EM_connect }; + static EventGenRec *nextGen = &alarmGen; static sigjmp_buf close_env; + ExtFunc volatile void die(char *msg) { perror(msg); @@ -101,10 +113,25 @@ ExtFunc int MyWrite(int fd, void *data, int len) return len; } //MyWrite +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 + static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event) { return E_alarm; -} +} //AlarmGenFunc ExtFunc void AddEventGen(EventGenRec *gen) { @@ -125,6 +152,39 @@ ExtFunc void RemoveEventGen(EventGenRec *gen) } } //RemoveEventGen +ExtFunc void AtExit(void (*handler)(void)) +{ //setup something to do at exit (^C) +#ifdef HAS_ON_EXIT + on_exit((void *)handler, NULL); +#else + atexit(handler); +#endif +} //AtExit + +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; + + fprintf(stderr, "- Closing connections...\n"); + for (i = 1; i < MAX_SCREENS; i++) + SCloseNet(i); //bye everybuddy + fprintf(stderr, "* All Done\n\n"); +} //CloseNets + ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask) { //poll int i, retry = 0; @@ -178,13 +238,6 @@ ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask) } } //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; @@ -194,9 +247,8 @@ ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event) result = MyRead(gen->fd, gen->buf + gen->bufSize, gen->bufGoal - gen->bufSize); if (result < 0) { - ByeClient(gen->player); - type = NP_endConn; - return E_net; + fprintf(stderr, "- Closed connection to player #%d\n", gen->player); + return E_lostConn; } gen->bufSize += result; if (gen->bufSize < gen->bufGoal) @@ -215,206 +267,228 @@ ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event) 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); + if (type == NP_endConn) { + fprintf(stderr, "- Quit player #%d\n", gen->player); + return E_lostConn; + } //client sent quit signal 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) +static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event) { - 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"); + int new; addrLen = sizeof(addr); - for (i = 1; i <= Connections; i++) { - if ((netGen[i].fd = accept(sockListen, (struct sockaddr *)&addr, &addrLen)) < 0) + for (new = 1; new <= MAX_SCREENS; new++) + if (netGen[new].fd < 0) break; + if (new > Game.maxplayers) return; + + if ((netGen[new].fd = + accept(gen->fd, (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 + fprintf(stderr, "+ Connection: %s\n", inet_ntoa(addr.sin_addr)); + val2.l_onoff = 1; + val2.l_linger = 0; + setsockopt(netGen[new].fd, SOL_SOCKET, SO_LINGER,(void *)&val2, + sizeof(val2)); + AddEventGen(&netGen[new]); + netGen[new].player = event->u.net.uid = new; + { //new connection; netGen already initialized in GenFunc + struct hostent *host; + + sprintf(Players[new].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[new].host, host->h_name, + sizeof(Players[new].host) - 1); + Players[new].host[sizeof(Players[new].host) - 1] = 0; + } //set player's hostname + } + } //E_connect + return E_connect; +} //ConnGenFunc ExtFunc int StartServer(void) { MyEvent event; + netint2 currentpiece[MAX_SCREENS]; int playercount; int playersReady = 0; int i; - { - char serverdata[255]; - for (i = 1; i <= totalPlayers; i++) { - sprintf(serverdata, "Netris server %s", version_string); - SendPacketTo(i, i, NP_hello, strlen(serverdata)+1, serverdata); - } - } - do { - 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 :( - Players[event.u.net.sender].alive = 0; - for (i = 1; i <= totalPlayers; i++) + switch (WaitMyEvent(&event, EM_any)) { + case E_lostConn: //client went away :( + Players[event.u.net.sender].alive = 0; + for (i = 1; i < MAX_SCREENS; i++) + if (Players[i].alive) SendPacketTo(i, event.u.net.sender, NP_argghhh, sizeof(Players[0].alive), &Players[event.u.net.sender].alive); - 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); - if (!Players[event.u.net.sender].team) - Players[event.u.net.sender].team = 256 - event.u.net.sender; - 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); - SendPacketTo(event.u.net.sender, 0, NP_gamedata, - sizeof(Game.seed)+sizeof(Game.initspeed), &Game); - 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 (++playersReady >= totalPlayers) { - fprintf(stderr, "Starting game (%010d)\n", Game.seed); - for (i = 1; i <= totalPlayers; i++) - SendPacketTo(i, 0, NP_goAhead, 0, NULL); - } //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) - if (event.u.net.type != NP_giveJunk - || Players[i].team != Players[event.u.net.sender].team) - SendPacketTo(i, event.u.net.sender, event.u.net.type, - event.u.net.size, event.u.net.data); - break; + SCloseNet(event.u.net.sender); + break; //NP_endConn + case E_net: + if (verbose) fprintf(stderr, ": %d sent %d\n", + netGen[event.u.net.sender].fd, event.u.net.type); + switch(event.u.net.type) { + case NP_hello: +// if (event.u.net.type != NP_hello) ByeClient(new); + { + 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, "= Wrong version player #%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 player #%d\n", + event.u.net.sender); + break; + } //NP_hello + case NP_newPlayer: + //receive player details and return other players + memcpy(&Players[event.u.net.sender], + event.u.net.data, event.u.net.size); + if (!Players[event.u.net.sender].team) + Players[event.u.net.sender].team = + 256 - event.u.net.sender; + if (Game.started < 2) + Players[event.u.net.sender].flags |= SCF_paused; + if (!Game.continuous && Game.started >= 2) { + char data[40]; + strcpy(data,"Can't join: Game has already started"); + fprintf(stderr, "- Can't join player #%d in " + "non-continuous game\n", event.u.net.sender); + SendPacketTo(event.u.net.sender, 0, NP_error, + strlen(data)+1, data); +// SCloseNet(event.u.net.sender, 0); + break; + } //can't join started game + { + static struct { + int playerflags; + int maxplayers; //1 + int started; //2 + int continuous; //3 + long seed; //4 + int initspeed; //5 + } data; + + memcpy(&data, &Players[event.u.net.sender].flags, + sizeof(data.playerflags)); + memcpy(&data.maxplayers, &Game, + sizeof(data) - sizeof(data.playerflags)); + SendPacketTo(event.u.net.sender, 0, NP_gamedata, + sizeof(data), &data); + } //send game options + for (i = 1; i < MAX_SCREENS; i++) + if (netGen[i].fd >= 0 && i != event.u.net.sender) { + SendPacketTo(event.u.net.sender, i, + NP_newPlayer, sizeof(Player) + - sizeof(Players[0].spy) + - sizeof(Players[0].small), + &Players[i]); + SendPacketTo(event.u.net.sender, i, + NP_newPiece, sizeof(currentpiece[i]), + ¤tpiece[i]); + SendPacketTo(i, event.u.net.sender, + NP_newPlayer, sizeof(Player) + - sizeof(Players[0].spy) + - sizeof(Players[0].small), + &Players[event.u.net.sender]); + } //send (to) players + fprintf(stderr, "> Joined player #%d: %s <%s>\n", + event.u.net.sender, + Players[event.u.net.sender].name, + Players[event.u.net.sender].host); + if (++playersReady >= minplayers) { + if (Game.started) + SendPacketTo(event.u.net.sender, 0, + NP_start, 0, NULL); + else { + fprintf(stderr, "* Starting game (%010d)\n", + Game.seed); + for (i = 1; i < MAX_SCREENS; i++) + SendPacketTo(i, 0, NP_start, 0, NULL); + Game.started++; + } //first goahead (to all) + } //give go ahead + break; //NP_playerdata + case NP_newPiece: + memcpy(¤tpiece[event.u.net.sender], + event.u.net.data, sizeof(currentpiece[0])); + goto sendtoall; + case NP_argghhh: + Players[event.u.net.sender].alive = 0; + fprintf(stderr, "< Player #%d died\n", + event.u.net.sender); + //check for unpaused game + case NP_pause: + { + int paused; + Players[event.u.net.sender].flags ^= SCF_paused; + paused = Game.started < 1; + for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive) + paused |= Players[i].flags & SCF_paused; + if (paused) paused = 1; + else if (Game.started == 1) Game.started++; + goto sendtoall; + } //NP_pause + default: //relay data to all players + sendtoall: +// if (event.u.net.type >= NP_pause) + for (i = 1; i < MAX_SCREENS; i++) + if (i != event.u.net.sender) + if (event.u.net.type != NP_giveJunk || + Players[i].team != Players[event.u.net.sender].team) + SendPacketTo(i, event.u.net.sender, + event.u.net.type, event.u.net.size, + event.u.net.data); + break; //>=NP_paused } - } - } //E_net - playercount = 0; - for (i = 1; i <= totalPlayers; i++) - if (netGen[i].fd >= 0) playercount++; - } while (playercount > 1); + break; //E_net + case E_connect: + { //new connection; netGen already initialized in GenFunc + char serverdata[255]; + + sprintf(serverdata, "Netris server %s", version_string); + SendPacketTo(event.u.net.uid, event.u.net.uid, NP_hello, + strlen(serverdata)+1, serverdata); + break; + } //E_connect + } //event + { + int j; + playercount = 0; + for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive) { + if (Players[i].team < 128) for (j = 1; j < i; j++) + if (Players[j].alive + && (Players[j].team == Players[i].team)) { + playercount--; + break; + } //player of same team counted before + playercount++; + } //player alive + } //count players + } while (Game.started < 2 || playercount > 1 || Game.continuous); + fprintf(stderr, "* Exiting server\n"); } //StartServer @@ -438,6 +512,9 @@ ExtFunc void Usage(void) "\n" " -s, --seed \tStart with given random seed\n" " -i, --speed \tSet the initial step-down interval, in seconds\n" + " -m, --min-players <2>\tNumber of players required before starting the game\n" + " -x, --max-players <8>\tMaximum number of players allowed in the game\n" + " -c, --continuous\tDon'n quit the game\n" "\n", DEFAULT_PORT); } @@ -480,11 +557,22 @@ ExtFunc void WriteConf(void) ExtFunc void HandleOption(char tag, char *value) { switch (tag) { + case 'v': //verbose + verbose = 1; + break; case 'p': //port port = atoi(value); break; - case 'c': //connections - Connections = atoi(value); + case 'c': //min-players + Game.continuous = atoi(value); + break; + case 'm': //min-players + minplayers = atoi(value); + break; + case 'x': //max-players + Game.maxplayers = atoi(value); + if (Game.maxplayers >= MAX_SCREENS) + Game.maxplayers = MAX_SCREENS; break; case 'i': //speed (of level 1) Game.initspeed = atof(value) * 1e6; @@ -515,7 +603,8 @@ ExtFunc void ReadConf(char *filename) 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) + if (buf[i] == ' ' || buf[i] == '\t' + || buf[i] == '\n' || buf[i] == 13) buf[i] = '\0'; else break; @@ -547,6 +636,7 @@ ExtFunc int main(int argc, char **argv) if (sigsetjmp(close_env, 1)) exit(0); signal(SIGINT, CatchInt); port = DEFAULT_PORT; + Game.maxplayers = 8; Game.initspeed = DEFAULT_INTERVAL; Game.seed = time(0); @@ -555,7 +645,7 @@ ExtFunc int main(int argc, char **argv) // else ReadConf(CONFIG_FILE); while ((ch = getopt_long(argc, argv, - "hHp:i:s:c:", options, NULL)) != -1) + "hHvp:i:s:c:m:x:", options, NULL)) != -1) HandleOption(ch, optarg); if (optind < argc) { Usage(); @@ -564,8 +654,38 @@ ExtFunc int main(int argc, char **argv) // WriteConf(); Header(); - WaitForConnection(port); - StartServer(); + + { + int i; + + for (i = 1; i < MAX_SCREENS; i++) + memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec)); + } //setup netGen var + + AtExit(CloseNets); + + { + int val1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if ((connGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + die("socket"); + val1 = 1; + setsockopt(connGen.fd, SOL_SOCKET, SO_REUSEADDR, + (void *)&val1, sizeof(val1)); + if (bind(connGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + die("bind"); + if (listen(connGen.fd, 1) < 0) + die("listen"); + + AddEventGen(&connGen); + } //setup listening sock + + StartServer(); //server loop + return 0; }