//======================================== // ServerInterface.C // // Server interface for ZNibbles // // ZNibbles // Vincent Mallet 1997, 1998, 1999 //======================================== // $Id: ServerInterface.C,v 1.10 1999/05/11 02:13:52 vmallet Exp $ /* ZNibbles - a small multiplayer game * Copyright (C) 1997, 1998, 1999 Vincent Mallet - vmallet@enst.fr * * 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. */ // Very ugly for now ! // And it could br optimized... // Well, I'll do better when I'll have time for it.. #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #endif #include "DLList.h" // Doubly linked lists #include "Options.H" #include "Common.H" #include "Nibble.H" #include "Player.H" #include "Trame.H" #include "World.H" #include "creer_socket.h" #include "ServerInterface.H" // static World * curworld = NULL; // pour faire des mesures avec sigalarm (crade mais debug) // static time_t startcycle = 0; // pour faire des mesures avec sigalarm // static int pipe_rw_error = 0; // constructor ServerInterface::ServerInterface(World& wr) : BaseInterface(wr), nb_pending(0), ival(120000), debug(0) { } // initialize this interface void ServerInterface::init(int argc, char *argv[]) { struct sigaction action; struct sockaddr_in address; int port; Options & options = * new Options(); // we already have called gtk_init() in the ClientGtk.C main function. options.set_option_set(OPTIONS_SERVER_SET); if (!options.parse(argc, argv)) { fprintf(stderr, "Usage: %s [OPTION]..\n\n", *argv); fprintf(stderr, "Try `%s --help` for more options.\n", *argv); exit(2); } if (options.is_help()) { display_help(*argv); exit(0); } if (options.is_version()) { display_version_short(); exit(0); } debug = options.is_debug(); port = options.get_port(); world.server_make(options.get_width(), options.get_height(), 100000, // max number of players 100000); // max number of objexts // do we need computer controlled worms ? world.server_set_no_computer(options.is_no_computer()); display_version(); std::cerr << "ServerInterface(): experimental nibbles" \ << "interface! Don't run it in background... " \ << std::endl << std::endl; srand(time(NULL)); memset(&action, 0, sizeof(action)); action.sa_handler = pipe_handler; sigaction(SIGPIPE, &action, NULL); // action.sa_handler = alarm_handler; // sigaction(SIGALRM, &action, NULL); /* // Detach from terminal if (fork() != 0) exit(0); setsid(); printf("server with pid %d launched \n", (int) getpid()); */ /* // Attachement du handler du signal SIGCHLD action.sa_handler = eliminer_zombie; sigaction(SIGCHLD, &action, NULL); */ std::cout << "Starting ZNibbles Server on port: " << port << std::endl; // Create and attach the listening socket if((socket_ecoute = creer_socket(SOCK_STREAM, &port, &address)) == -1) { std::cerr << "Unable to create socket. Try option `--help' to get some help."\ << std::endl; exit(2); } if (debug) printf("okay. socket fd = %d\n", socket_ecoute); // Start listening to the socket if(listen(socket_ecoute,10) == -1) { perror("listen"); exit(2); } if (debug) world.display(); } // display version numbers (long format) void ServerInterface::display_version() { std::cerr << "ZNibbles Server v" VERSION " - A little silly game - " << "(c) Vincent Mallet 1997, 1998, 1999 - vmallet@enst.fr" << std::endl << std::endl; } // display version number (short format) void ServerInterface::display_version_short() { std::cout << "ZNibbles Server " VERSION << std::endl; } // display small help void ServerInterface::display_help(char *name) { std::cout << "usage: " << name << " [OPTIONS].." << std::endl; std::cout << std::endl; std::cout << "Start a ZNibbles Server." << std::endl; std::cout << std::endl; std::cout << " -p, --port=NUM listen for clients on port NUM [default is 5051]" << std::endl; std::cout << " -s, --size=NUMxNUM create a world of dimension NUMxNUM [default is 80x40]" << std::endl; std::cout << " -w, --width=NUM create a world of width NUM [default is 80]" << std::endl; std::cout << " -g, --height=NUM create a world of height NUM [default is 40]" << std::endl; std::cout << " -c, --no-computer disable computer-controlled worms" << std::endl; std::cout << " -d, --debug enable debug output" << std::endl; std::cout << " -V, --version print version number, then exit" << std::endl; std::cout << " -h, --help show this message and exit" << std::endl; std::cout << std::endl; std::cout << "Report bugs to ." << std::endl; } #define MAXLINE 225 // Run the Server Interface. This will launch the whole // server thing, and should in fact never return. void ServerInterface::run() { struct sockaddr_in address; int lg_address = sizeof address; int socket_service; char line[MAXLINE]; Trame t; int xpaused = 0; // boolean : FALSE int done = 0; // boolean : FALSE while (!done) { display_system_message("Sleeping while waiting for someone...\n"); xpaused = 0; // FALSE socket_service = -1; while (socket_service == -1) { // wait for connection #if defined(__FreeBSD__) && (__FreeBSD_version >= 400013) socket_service = accept(socket_ecoute, (struct sockaddr *) &address, (socklen_t *) &lg_address); #else socket_service = accept(socket_ecoute, (struct sockaddr *) &address, &lg_address); #endif // Reception d'un signal (probablement SIGCHLD) if(socket_service == -1 && errno == EINTR) { display_system_message("Got a signal (probably SIGCHLD)\n"); continue; } // Erreur plus grave if(socket_service == -1) { perror("accept"); exit(2); } break; } display_system_message("Accepted a connection\n"); pending_socket_list.append(socket_service); nb_pending++; int cpt = 0; while (!done && (world.nbplayers || nb_pending)) { cpt = (cpt + 1) % 5; if (!cpt) { accept_new(socket_ecoute); if (nb_pending) for (Pix p = pending_socket_list.first(); p; ) { if (handle_new_connection(pending_socket_list(p))) { pending_socket_list.del(p, -1); nb_pending--; } // advance to next in list if (p != NULL) pending_socket_list.next(p); else p = pending_socket_list.first(); } } if (read_ready(STDIN_FILENO, ival)) { char *peol; if (debug) { std::cout << "chaine: "; } fgets(line, MAXLINE, stdin); if (NULL != (peol = strchr(line, '\n'))) *peol = '\0'; if (strcmp(line, "quit") == 0) { display_system_message("Server shutting down'n"); world.quit_game(); done = 1; continue; } else if (strcmp(line, "cycle0") == 0) { world.server_cycle(); // t.reset(); // t.put_short(CYCLE_NOTIFY); world.send_to_all(world.cycle_trame); world.get_client_responses(); continue; // } else if (strcmp(line, "time") == 0) { // ival = 0; // booste au max // startcycle = world.playcycle; // set_timer(10); // 10 secondes de timing // continue; } else if (strcmp(line, "p") == 0) { // pause xpaused = !xpaused; display_system_message(((xpaused) ? (char *)"Pausing...\n" : (char *)"Running...\n")); continue; } else if (strcmp(line, "-") == 0) { // slower... if (debug) { std::cout << "slower... ival="; } ival += 25000UL; if (debug) { std::cout << ival << std::endl; } continue; } else if (strcmp(line, "+") == 0) { // faster! if (ival > 0UL) { if (debug) { std::cout << "Faster! ival="; } ival -= 25000UL; if (ival > 10000000) ival = 0; if (debug) { std::cout << ival << std::endl; } } else display_system_message("Can't go faster :)\n"); continue; } else if (strcmp(line, "W") == 0) { // pause xpaused = 1; // TRUE display_system_message("Pausing...\n"); t.reset(); t.put_char(VOID_TRAME); t.put_string("w"); world.send_to_all(t); world.display(); continue; } t.reset(); t.put_char(VOID_TRAME); t.put_string(line); } else { if (!xpaused) { // struct tms tms1, tms2; // clock_t tt, uu; // tt = times (&tms1); world.get_client_responses(); world.server_cycle(); world.send_to_all(world.cycle_trame); // if (! (world.playcycle % 20)) { // printf("cycle=%d\r", (int) world.playcycle); // fflush(stdout); // } // uu = times (&tms2); // if (! (world.playcycle % 40)) { // printf("tt=%ld uu=%ld ut1=%ld ut2=%ld \n", // (long) tt, (long) uu, (long) tms1.tms_stime, (long) tms2.tms_stime); // printf("t1-t2=%ld\n", (long) uu - tt); // } } continue; } world.send_to_all(t); } } display_system_message("Server closing listen service\n"); close(socket_ecoute); } // called when a "broken pipe" signal is received void ServerInterface::pipe_handler(int sig) { sig++; // std::cerr << "got SIGPIPE!" << std::endl; // pipe_rw_error = 1; } void ServerInterface::set_timer(int nbsecs) { struct itimerval itv; itv.it_value.tv_sec = nbsecs; itv.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &itv, NULL); } // void ServerInterface::alarm_handler(int sig) // { // if (curworld) { // std::cout << "---- TIMED: " << (curworld->playcycle - startcycle) // << " cycles -------" // << "(sig=" << sig << ")" << std::endl; // startcycle = curworld->playcycle; // } // } // void eliminer_zombie(int sig){ // printf("terminaison d'un processus de service \n"); // wait(NULL); // } int ServerInterface::accept_new(int socketnum) { struct sockaddr_in address; int lg_address; int socket_service; int nb_accepted = 0; lg_address = sizeof(address); while (read_ready(socketnum)) { #if defined(__FreeBSD__) && (__FreeBSD_version >= 400013) socket_service = accept(socketnum, (struct sockaddr *) &address, (socklen_t *) &lg_address); #else socket_service = accept(socketnum, (struct sockaddr *) &address, &lg_address); #endif // s'il y a une erreur, ce n'est pas une EINTR (cf appel a read_ready()) if(socket_service == -1) { perror("accept failed"); exit(2); } display_system_message("Connection accepted..\n"); nb_accepted++; pending_socket_list.append(socket_service); nb_pending++; } return nb_accepted; } int ServerInterface::handle_new_connection(int socketnum) { display_system_message("-- handle new connect --\n"); if (read_ready(socketnum)) { world.server_add_player(socketnum); return 1; } return 0; } void ServerInterface::display_message(Player& from, char *msg, int priv) { if (!debug) return; if (priv) std::cout << "*private* Message: " << from.get_name() << "> " << msg << std::endl; else std::cout << "Message: " << from.get_name() << "> " << msg << std::endl; } void ServerInterface::display_system_message(char * msg, Player * p, int color) // default p=NULL, color=0 { if (!debug) return; color++; std::cout << "*** "; if (p) std::cout << p->get_name() << " "; std::cout << msg; }