Path: utzoo!attcan!uunet!island!argv From: argv@island.uu.net (Dan Heller) Newsgroups: comp.sources.x Subject: v05i031: Tron - Multiplayer Game, Part01/02 Message-ID: <1207@island.uu.net> Date: 22 Nov 89 01:12:32 GMT Organization: Island Graphics, Marin County, California Lines: 937 Approved: island!argv@sun.com Submitted-by: Helmut Hoenig Posting-number: Volume 5, Issue 31 Archive-name: tron/part01 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'read_me.txt' <<'END_OF_FILE' X X ++++++++++++ X +++ TRON +++ - an XWindows game X ++++++++++++ (colored or black&white) X Xby Helmut Hoenig (MAY-1988) X X X This is an X-version of the well known light-race of Xthe TRON-film. I actually wrote that program to get a bit Xmore familiar with X and with communication between programs Xthrough sockets. (That should be an excuse for writing pro- Xgrams with such a bad structure.) But at least it runs and Xbesides the programming-experience I got, we already had a Xlot of fun with it. X X After we tested the program for more than a year, I Xfinally decided to post it. To play that game you need: XUNIX, XWindows X11R3, a C-compiler, a lot of include-files. X X The makefile will help you to compile the 4 source- Xfiles into the 2 binary-files "tron_server" and "tron_run". XThese files have to be available in the same directory on Xevery host, where the program should be used (e.g. X/usr/games), since the programs are getting started via a Xremote shell using a system()-call. (see "tron_server.c", Xline 284ff) X X X+++ HOW IT WORKS +++ (you might skip this if it works) X X The "tron_server"-program is used to start the system Xup. I also call it the master. In the startup-sequence, a X"tron_run"-program is started for each player (usually on Xdifferent hosts). Each program connects itself to the mas- Xter. Through that stream-connection, the master is able to Xsend commands to the players (e.g. start game) and the Xplayers can answer with the number of rounds they played Xuntil they were busted. X X After the startup-sequence, we have a star- Xconfiguration of the players and the master. Each player Xsends the port-number of an additional datagram-socket to Xthe master, which is used during the game. The master opens Xa window to select the players and start the game. When the Xgame gets started, every player-program receives the port- Xnumber and host of the next player. In that way a datagram- Xpaket, produced by the first player, can be send around in a Xring. X X No effort (except a timeout, which ends the game) has Xbeen made to recover from missing datagrams, since it seems Xthat they don't get lost on our system. They just come a bit Xlate sometimes and then there's no sense in playing anyway. X X X+++ WHAT TO KNOW +++ X X The game is made for up to 8 players. At least one of Xthem has to be human. X X The players control their racers by mouse or keyboard. XThe keys to use are available from the program. X X Even if you play with the keyboard, you've got to place Xyour racer with the mouse (and middle button) in the field. XIt starts running in the direction of your mouse. If you Xwait a few seconds, the racers are automatically placed Xunder your mouse and the game starts. X X For each player you can enter the display- and the Xhost-name (separated by a '^', e.g. 'display:0.0^host'). If Xthere is no display-name given the host is used for an Xauto-player only (e.g. '^host'). As you usually play on the Xhost of the display, a single name is enough to select host Xand display. (So the usual command-line looks like X'tron_server host1 host2 ^auto_player1 ...) You might get Xproblems in the starting up sequence, if there are too many Xauto-players on the same host. X X X+++ PROBLEMS +++ X X On our system, we sometimes have problems in the Xstartup-sequence as there seem to be a lot processes Xenvolved when starting the remote-shells. It happen, that Xthe startup fails, when there are too many players (auto- Xplayers) on the same host. To avoid that it is possible to Xlet the server sleep some seconds between the starting of Xplayers by the option '-s'. X X When controlling with the mouse, you might get problems Xwith your window-manager, if it grabs combinations of Xshift/control and mouse-buttons. Play with keyboards or kill Xthe window-manager in that case. X X X+++ HAVE FUN +++ X X Helmut Hoenig X XPS.: X XTRON runs on our system with suns and MicroVaxes. It is Xtested on 3 kinds of monochrom-displays and a colored one. X XPlease let me know if TRON runs on your system ! XMy address: X hoenig@informatik.uni-kl.de END_OF_FILE if test 4246 -ne `wc -c <'read_me.txt'`; then echo shar: \"'read_me.txt'\" unpacked with wrong size! fi # end of 'read_me.txt' fi if test -f 'makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile'\" else echo shar: Extracting \"'makefile'\" \(889 characters\) sed "s/^X//" >'makefile' <<'END_OF_FILE' X# Tron consists of 2 program files. The 'tron_server' is used to start up X# the system. It starts a 'tron_run'-program for each player. For that it X# gets the file-name of it through the TRON_RUN_FILE-Macro. That name has X# to be the same on every host (e.g. /usr/games). X# The SOCKET_NAME-Macro is used, but not really nescessary. X XCFLAGS = -g XOBJ1 = tron_run.o texter.o helps.o XOBJ2 = tron_server.o texter.o helps.o X Xmain: tron_run tron_server X Xtron_run: $(OBJ1) X $(CC) $(CFLAGS) $(OBJ1) -lX11 -o tron_run X Xtron_server: $(OBJ2) X $(CC) $(CFLAGS) $(OBJ2) -lX11 -o tron_server X Xtron_run.o: grey_maps.h messages.h header.h X Xtron_server.o: messages.h header.h X $(CC) $(CFLAGS) \ X -DSOCKET_NAME=\"tron\" \ X -DTRON_RUN_FILE=\"~hoenig/sockets/tron_run\" \ X -c tron_server.c X Xtexter.o: header.h X $(CC) $(CFLAGS) \ X -DFONT_NAME=\"-*-courier-bold-r-normal--24-*-*-*-*-*-*-*\" \ X -c texter.c END_OF_FILE if test 889 -ne `wc -c <'makefile'`; then echo shar: \"'makefile'\" unpacked with wrong size! fi # end of 'makefile' fi if test -f 'tron_server.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tron_server.c'\" else echo shar: Extracting \"'tron_server.c'\" \(19352 characters\) sed "s/^X//" >'tron_server.c' <<'END_OF_FILE' X/* This program was written by Helmut Hoenig */ X/* in May of 1988 at the Unitversity of */ X/* Kaiserslautern, Germany. It may be copied freely. */ X X#define moremessages 0 X X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#include "header.h" X#include "messages.h" X Xshort readshort(); X X#define HMAX 8 Xint max; /* when starting the system, this is the actual number of players */ X Xint messflag; X#define MAXPLAYERS 10 X X/* fields for result-messages */ Xstatic int points[MAXPLAYERS]; Xstatic int place[MAXPLAYERS]; Xstatic int count[MAXPLAYERS]; Xstatic int sorted[MAXPLAYERS]; Xstatic char tscreen[20][80]; Xstatic int lines; Xstatic int players; X Xstruct hostent *hostaddr[HMAX]; Xint active[HMAX]={ 0,0,0,0,0,0,0,0 }; Xint auto_only[HMAX]={ 0,0,0,0,0,0,0,0 }; X Xchar host_name[HMAX][20]; Xchar disp_name[HMAX][20]; X Xint sockets[HMAX],ports[HMAX]; Xlong play_time[HMAX]; Xstruct sockaddr_in rsin[HMAX]; Xint rsinlen[HMAX]; Xint socks[HMAX]; X X Xchar *my_host; /* eigener hostname */ X Xstruct servent *sp; X Xstruct sockaddr_in sin; Xint mastersocket; X Xchar *getenv(); X Xlong packet_count = 0; Xint data_error=0; Xint g_count=0; Xint match; X XDisplay *display; XScreen *screen; Xchar dispname[20]; Xextern XFontStruct *(finfo[]); Xextern Window aw; Xint waiting; XXEvent event; X#define FID 0 X Xusage() X{ X printf("\n\nusage:\ttron_server [ option ] \n"); X printf("\n\t-v - prints some additional messages in starting sequence.\n"); X printf("\t-h - prints this.\n"); X printf("\t-s - to sleep seconds after the start of each players.\n"); X printf("\n: List with the display_names and/or host_names of the players.\n"); X printf(" If only one name per player is given, the display is the display of the host.\n"); X printf(" Otherwise you can write '^' to get a special display.\n"); X printf(" An empty display-name stands for an automatic player. (e.g. ^host)\n"); X printf("\n"); X} X Xmain(argc,argv) X int argc; X char *argv[]; X{ int i,j; X char command[200]; X int test; X int waitflag=0; X char *ptr; X char *disp_arg=NULL; XXSetWindowAttributes attrib; X X/********************************************************************* X * At first, the host-names of the command-line are tested. * X * If there is a display for the player (no auto-player), the server * X * tests the possibility of opening XWindows on the display. If that * X * fails or a display is used twice, the player is ignored. * X * ==> In the end, there should be at least 2 correct names. * X *********************************************************************/ X X max=0; X messflag=0; X printf("\nTRON - programmed by Helmut Hoenig. (MAY-1988)\n"); X printf("startup-sequence at the following displays:\n"); X X if (argc==1) X { usage(); X exit(); X }; X X for (i=1;is_port; X sin.sin_addr.s_addr= INADDR_ANY; X }; X X/* Namensbindung an den Socket */ X if (bind ( mastersocket,&sin,sizeof sin)<0) X { fprintf(stderr,"### #%s: error in bind.\n",my_host); X do X { sin.sin_port++; X } X while (bind (mastersocket,&sin,sizeof sin)<0); X fprintf(stderr,"### #%s: port %d selected as an alternative.\n",my_host,ntohs(sin.sin_port)); X }; X if (messflag) printf("*** master-socket is %s@%d. ***\n",my_host,(int)ntohs(sin.sin_port)); X X/* Einrichten einer Warteschlange */ X listen(mastersocket,max); X X/****************************************************************** X * The 'tron_run'-Program will now be started on each player-host.* X * A Bell-Signal is given on each display. * X ******************************************************************/ X X for (i=0;i &' Xwith - host for a player X - name of the 'tron_run'-binary-file on that host X the macro TRON_RUN_FILE from the top off that source-file is used. X - my host (for the stream-connection) X - my socket (for the stream-connection) X - the display, usually :0.0 X - to get the players in an order X - to produce additional messages by the players X***********************************************************************************/ X sprintf(command,"%s '%s %s %d %s %d %d' &", X host_name[i], X TRON_RUN_FILE, X my_host, X (int)ntohs(sin.sin_port), X disp_name[i], X i, X messflag); X X if (messflag) printf("#%s: starting up %s on %s.\n",my_host,disp_name[i],host_name[i]); X#if (moremessages) X printf("%s\n",command); X#endif X system(command); X if (waitflag) X { printf("sleeping %d seconds.\n",waitflag); X fflush(stdout); X sleep(waitflag); X } X } X X printf("#%s: %d clients started.\n",my_host,max); X X/******************************************************************************** X * The server waits, until all players are started and connected to the server. * X * Each Player-Program returns a port-number, which it uses in the game. * X ********************************************************************************/ X X for (i=0;i-argument X of the player's command-line. ***/ X sockets[count]=socks[i]; X ports[count]=(int)readshort(sockets[count]); X auto_player = readint(sockets[count]); X if (auto_player!=auto_only[count]) X { fprintf(stderr,"*** no XWindows on %s -> auto-player-mode only\n",disp_name[count]); X auto_only[count]=auto_player; X }; X if (messflag) printf("#%s: connection %s@%d established.\n",my_host,disp_name[count],ports[count]); X }; X printf("#%s: all clients connected.\n",my_host); X X close(mastersocket); X X/********************************************************** X * After all players are running, a window can be openend * X * to select who is really playing. * X **********************************************************/ X X if ((display=XOpenDisplay(disp_arg))==NULL) X { fprintf(stderr,"*** %s: Can't open display.\n",my_host); X exit(0); X }; X screen=XScreenOfDisplay(display,DefaultScreen(display)); X init_texter(); X X aw=XCreateSimpleWindow(display,RootWindowOfScreen(screen), X 20,50,26 * CharWidth(FID),(max+3+7) * CharHeight(FID), X 4,XBlackPixelOfScreen(screen),XWhitePixelOfScreen(screen)); X attrib.override_redirect = 1; X XChangeWindowAttributes(display,aw,CWOverrideRedirect,&attrib); X XSelectInput(display,aw,ExposureMask | ButtonPressMask); X X/************************************************************************* X * The next loop is executed, until QUIT is selected in the main menu. * X * You have to wait until a match is ended to get back to the main menu. * X *************************************************************************/ X for(;;) X { long tp1,tp2; X X if (data_error) X prepare_message(); X else X prepare_scores(); X send_scores(); X X if (match) X { int root_x,root_y,x,y; X Window root,child; X int mask; X X sleep(2); X /*********************************************************** X * you CAN STOP a match by holding down the left AND right * X * button on your mouse, befor the next game starts. * X ***********************************************************/ X X XQueryPointer(display,ROOT,&root,&child,&root_x,&root_y,&x,&y,&mask); X if ((mask & Button3Mask) && (mask & Button1Mask)) X { match=0; X sprintf(tscreen[0],"Match ended."); X send_scores(); X }; X }; X X if (!match) X { X XMapRaised(display,aw); X waiting=1; X do X { X XWindowEvent(display,aw,ExposureMask | ButtonPressMask,&event); X aw_event(event.type); X } X while(waiting); X XUnmapWindow(display,aw); X }; X tp1=time((long)0); X send_game_start(); X wait_players_ready(); X wait_game_over(); X tp2=time((long)0); X X for(i=0;ipoints[*b]) return(-1); X return(0); X} X X/************************************************ X * preparation of a text-field with the * X * sorted list of players with their scores. * X ************************************************/ Xprepare_scores() X{ int i; X X/* Spieler nach Punkten sortieren */ X players=0; X for (i=0;i=mp) && (points[sorted[0]]>points[sorted[1]]+1)) X { match=0; X sprintf(tscreen[0],"-- Final Result --"); X } X else X sprintf(tscreen[0],"Points: (%d)",mp); X } X else X { X sprintf(tscreen[0],"Points:"); X }; X X sprintf(tscreen[1],""); X X lines=2; X for (i=0;i> no turns while booster's on. <<"); X lines=11; X send_scores(); X}; X Xprepare_message() X{ X sprintf(tscreen[0],"- Timeout -"); X lines=1; X} X X/********************************************* X * sends the textfield to all active players * X *********************************************/ Xsend_scores() X{ int i,l; X X for (i=0;icount[*b]) return(-1); X return(0); X} X X/*************************************************** X * waits, until the game is over. * X * the server receives a counter from each player, * X * which says, when it was destroyed. * X ***************************************************/ Xwait_game_over() X{ Xint readfds,writefds,execptfds; Xstruct timeval timeout; X Xint ready_count=0,i,nfound; X X data_error=0; X while(ready_countYES->AUTO->NO */ X if (auto_only[y]) X { if (active[y]==1) active[y]++; X } X if (active[y] & 2) writeint(sockets[y],UNMAP); X } X else X { y-=max+1; X if (y<2) /* START GAME OR MATCH */ X { int i1,i2,j; X X i1=i2=0; X for (j=0;j1)&&(i2>0)) X { waiting=0; X match=(y==0); X } X else X { XBell(display,50); X XFlush(display); X } X }; X if (y==2) /* SHOW KEYS */ X { int t; X for (t=0;t