SERVER PATCHES ---------------------------------------------------------------------- Newsgroups: alt.games.xtrek From: terence@bronco.ece.cmu.edu (Terence Chang) Subject: Getting a server working: startd Date: Fri, 8 Nov 1991 13:34:09 GMT This is turning into FAQ #1 on starting a server. The distribution startd.c, more likely than not, will not function correctly, for various reasons. Attached below is a more refined version that I use on bronco.ece.cmu.edu. Changes are documented. Terence /* * newstartd.c * * Derived from distribution startd.c and xtrekII.sock.c (as of ~1/14/91). * * Installation: update the PROG and LOGFILE variables below, and * set the default port appropriately (presently 2592). Set debug=1 * to run this under dbx. Link this with access.o and subnet.o (and * remove xtrekII.sock.*). * * Summary of changes: * - subsumes xtrekII.sock into startd (newstartd calls ntserv directly to * reduce OS overhead) * - some debugger support code added * - uses reaper() to keep process table clean (removes procs) * * 8/16/91 Terence Chang */ #include #include #include #include #include #include #include #include #include #include #include #include #define PROG "/usr/users/terence/bin/ntserv" #define LOGFILE "/usr/users/terence/lib/xtrekII/xtreklog" int port = 2592; int debug = 0; int sock = -1; int newsock = -1; extern int errno; int connectionAttemptDetected(), reaper(); /* xtrek.sock global stuff: (TC) */ #define SP_BADVERSION 21 struct badversion_spacket { char type; char why; char pad2; char pad3; }; extern char peerhostname[]; /* Defined in access.c (TC) */ int fd; main(argc, argv) int argc; char **argv; { int tries=0; int forked; char startprog[255]; char sockstr[10]; /* xtrekII.sock main() declarations (TC) */ FILE *fp; long curtime; char logname[BUFSIZ]; struct badversion_spacket packet; int game; char version[81]; if(argc == 2) port = atoi(argv[1]); fprintf(stderr,"Accepting xtrek calls to port %d!\n", port); signal(SIGCHLD, reaper); if (!debug) detach(); fd = open(LOGFILE, O_CREAT | O_WRONLY | O_APPEND, 0600); keepontrying: while(!connectionAttemptDetected()){ fprintf(stderr, "Whoops. Bye.\n"); exit(0); } if (host_access(NULL)==0) { packet.type=SP_BADVERSION; packet.why=1; write(0, (char *) &packet, sizeof(packet)); time(&curtime); sprintf(logname, "Denied %-32.32s %s", peerhostname, ctime(&curtime)); if (fd!=-1) write(fd, logname, strlen(logname)); sleep(2); } else if (fork() == 0) { /* we are a clone */ time(&curtime); sprintf(logname, " %-32.32s %s", peerhostname, ctime(&curtime)); if (fd!=-1) write(fd, logname, strlen(logname)); if (fd!=-1) close(fd); execl(PROG, PROG, peerhostname, 0); fprintf(stderr,"Error in execl! -- \n"); reporterror(); } close(0); goto keepontrying; } int connectionAttemptDetected() { struct sockaddr_in addr; struct sockaddr_in naddr; struct timeval timeout; int readfds; int len; if(sock < 0){ if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0 ){ fprintf(stderr,"Hey! I can't make a socket!\n"); fprintf(stderr,"I'll try again \n"); return(0); } addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); if(bind(sock, &addr, sizeof(addr)) < 0){ if(errno == ENOTSOCK){ fprintf(stderr,"bogus! socket is unbindable!\n"); fprintf(stderr,"Used Port %d \n", port); reporterror(); close(sock); sock = -1; sleep(10); fprintf(stderr,"closing socket!"); return(0); } if (debug) fprintf(stderr, "Taking 10...\n"); sleep(10); if (bind(sock, &addr, sizeof(addr)) < 0) { if (debug) fprintf(stderr, "Taking 10...\n"); sleep(10); if (bind(sock, &addr, sizeof(addr)) < 0) { fprintf(stderr,"Bogus! I can't bind to the socket!\n"); fprintf(stderr,"Error: %d\n", errno); close(sock); return(0); } } } if(listen(sock, 5)<0) { fprintf(stderr,"Listen failed: "); reporterror(); } close(0); } len = sizeof(naddr); /* hack: to support reaper(), must ignore EINTR errors (TC) */ nointr: newsock = accept(sock, &naddr, &len); if ((newsock < 0) && (errno == EINTR)) goto nointr; if(newsock < 0){ fprintf(stderr,"accept error!"); reporterror(); fprintf(stderr,"No one calling!\n"); shutdown(sock, 2); close(sock); sock = -1; return(0); } else { if (newsock != 0) { if (dup2(newsock, 0) == -1) { fprintf(stderr, "failed dup2\n"); reporterror(); } close(newsock); } return(1); } } reporterror() { switch(errno){ case EBADF: fprintf(stderr,"EBADF!\n"); break; case ENOTSOCK: fprintf(stderr,"ENOTSOCK!\n"); break; case EOPNOTSUPP: fprintf(stderr,"EOPNOTSUPP!\n"); break; case EFAULT: fprintf(stderr,"EFAULT!\n"); break; case EWOULDBLOCK: fprintf(stderr,"EWOULDBLOCK!\n"); break; case ENOTDIR: fprintf(stderr,"ENOTDIR!\n"); break; case EINVAL: fprintf(stderr,"EEINVAL!\n"); break; case ENOENT: fprintf(stderr,"ENOENT!\n"); break; case ELOOP: fprintf(stderr,"ELOOP!\n"); break; case EACCES: fprintf(stderr,"EACCES!\n"); break; case ENOEXEC: fprintf(stderr,"ENOEXEC!\n"); break; case ENOMEM: fprintf(stderr,"ENOMEM!\n"); break; case EADDRNOTAVAIL: fprintf(stderr,"EADDRNOTAVAIL!\n"); break; case EADDRINUSE: fprintf(stderr,"EADDRINUSE!\n"); break; case EINTR: fprintf(stderr,"Interrupted system call.\n"); break; default: fprintf(stderr,"reporterror: unknown: %d\n",errno); } } detach() { int fd; fd = open("/dev/tty", O_RDWR, 0); if (fd < 0) return; (void) ioctl(fd, TIOCNOTTY, (char *) NULL); (void) close(fd); } reaper(sig) { if (debug) fprintf(stderr,"Reaping...\n"); while (wait3((union wait *) 0, WNOHANG, (struct rusage *) 0) > 0) ; } ------------------------------ From: coffee@ccwf.cc.utexas.edu (Steve Coffee) Newsgroups: alt.games.xtrek Subject: AI 'bot and defunct Date: 4 Nov 91 18:17:51 GMT On the subject of defunct processes: In startd.c there is a line with "fork" in it. Change it to the following: if (fork()) { (just remove the !) That should work. I had the same problem, but this fixed it. (Thanks ddt) Steve Coffee coffee@ccwf.cc.utexas.edu ------------------------------ From: hadley@aten.ics.uci.edu (Tedd Hadley) Newsgroups: alt.games.xtrek Subject: Re: defunct from startd Date: 4 Nov 91 20:12:29 GMT In karthy@iesd.auc.dk (Karsten Thygesen) writes: >I'm running the netrek server in Denmark. Every time a user have >played the game, I get a process. I'm NOT using inetd, but >instead startd. I fixed this on my server the other day. The README file wasn't quite correct for SunOS 4.1.1. The following changes should be made to startd.c At the top add: #include #include int reaper(); To the beginning of main() add: (void) signal(SIGCHLD, reaper); reaper() is: reaper(sig) { while (wait3((union wait *) 0, WNOHANG, (struct rusage *) 0) > 0) ; } In connectionAttemptDetected you need to change newsock = accept(sock, &naddr, &len); to: while( (newsock = accept(sock, &naddr, &len)) < 0 && errno == EINTR) ; Otherwise the SIGCHLD signal causes an interupted system call error in accept and startd exits. (There may be a more elegant way to fix this -- it worked so I left it) ++ Tedd Hadley (hadley@ics.uci.edu) ------------------------------ From: chiuk@iuvax.cs.indiana.edu (Kenneth Chiu) Newsgroups: alt.games.xtrek Subject: Re: AI 'bot and defunct Date: 4 Nov 91 20:49:11 GMT In article <60939@ut-emx.uucp> coffee@ccwf.cc.utexas.edu (Steve Coffee) writes: >On the subject of defunct processes: In startd.c there is a >line with "fork" in it. Change it to the following: > > if (fork()) { > >(just remove the !) > >That should work. I had the same problem, but this fixed it. >(Thanks ddt) For those of you who are curious as to why this works, this reverses the relative positions of the server and the startd process. So now only server processes will ever have children (they will mostly be other server processes). But the server already installs a signal handler to wait on zombie children. You will notice that the process id of the startd process keeps changing. ------------------------------ From: fadden@uts.amdahl.com (Andy McFadden) Newsgroups: alt.games.xtrek Subject: Re: defunct from startd Date: 4 Nov 91 21:49:05 GMT In article <1991Nov4.142836.11345@news.cs.indiana.edu> chiuk@iuvax.cs.indiana.edu (Kenneth Chiu) writes: #The servers are children of the startd process. When a child dies, it becomes #a zombie until the parent does a wait() on it. So the parent must notice when #children die (SIGCHLD) and then wait on them. # ##ell, I thought that startd (from scam) already did this waiting for you, but #I guess not. Check the source for startd and see if it has any kind of #"reaper" function that does this waiting. Also, somewhere, this "reaper" #must be installed as the signal handler for the SIGCHLD signal. No, it doesn't. If you add the "reaper()" function and trap SIGCHLD, the accept() system call will return with an error of EINTR. You have to catch this or startd will exit (I know, I did it two days ago). Just compare errno with EINTR and have it goto the top of the function. -- fadden@uts.amdahl.com (Andy McFadden) fadden@cory.berkeley.edu (expires in December) [ Above opinions are mine, Amdahl has nothing to do with them, etc, etc. ] ------------------------------ From: jesup@cbmvax.commodore.com (Randell Jesup) Newsgroups: alt.games.xtrek Subject: Re: ntserv bug in getentry.c Date: 20 Nov 91 03:04:08 GMT fadden@amdahl.uts.amdahl.com (Andy McFadden) writes: >/* if first time, check for tournament rules */ >for (i = 0; i < tournplayers; i++) { >So long as TOURN=4, everything will work as expected. This is probably why >the bug was never found. The fix is to substitute 4 for tournplayers in the >for condition. Better fix: substitute NUMTEAM for that (and the previous use of 4 in getentry.c). ------------------------------ From: doug@retzlaff.llnl.gov (Douglas S. Miller) Newsgroups: alt.games.xtrek Subject: How to set up a server Date: 15 Nov 91 22:10:53 GMT I apologize for the length of this posting, most of it is C code of interest only to those struggling to set up a server. As for the server I intended to make public at my site, we sys-admins have not come to an agreement about that yet. There are many questions about security that we need to resolve first. What follows are the notes I made while setting up a server on a DEC 5000 running Ultrix 4.1, plus a code from Kenneth Chiu that replaces startd. If this is confusing, well, it's just notes--I dont't have time to write it up nicely. I hope someone out there finds this useful. =========================================================================== 1) I suggest compiling netrek first--this usually goes without trouble and will give you a nice warm feeling that things are going well. This feeling should not be trusted. Do a 'make install' anyway. 2) Go to the ntserv directory, read the README, follow the directions slavishly, and compile. This will probably also work ok, with a few minor problems as noted below. Do a 'make install' again. 3) Go to the startd directory, read the README, again follow the directions carefully. Note the point made below about all the places you must change directory names in the source code to make things work. 4) You are now ready to run startd, which will fire up the server for you. If you are like a lot of people, it won't work. The best fix for this that I know of is to use the code supplied by Kenneth Chiu instead of startd. A copy is included below. These are notes I made while getting the Berkeley xtrek server distribution to run. Take them for what they are worth--your mileage will vary. ============================== Compile everything as in the directions. READ THE DAMN README's. I had to change the include statements that called for X/Xlib to X11/Xlib in the source files. If you are running X10 I suspect you won't have to do this. Result: Server now runs by hand but not in it's usual mode,i.e., people cannot login from outside. ============================= Note that defs.h is NOT the only place in the source where the games directory location is specified. Grep for 'games' in startup files, make changes. Isn't this a serious design glitch for a source intended to be portable? Result: None. Apparently this was a problem, but it wasn't the only problem. Startd could be 'hello world' for all the good it is doing. ================================ Got message from Kenneth Chiu, giving me new start daemon program. Result: It works beautifully. It is important to remember to kill all the old attempts to make things work, which is done by killing the old process named 'daemon'. Then restart things by running the 'listen' code. Try connecting with a client and watch the magic of xtrek absorb the rest of your free time until your girlfriend/wife drags you away... ================================ From chiuk@iuvax.cs.indiana.edu Fri Nov 8 13:44:43 1991 Date: Fri, 8 Nov 91 16:45:07 -0500 From: Kenneth Chiu To: doug@retzlaff.llnl.gov Subject: Re: Need Help with Setting Up Server A lot of people seem to have some problems. So did I. I decided it would be easier to re-write the startup code rather than trying to get startd to work. Anyway, here is my startup code. I call it listen.c, but you can call it anything you want. You might find it easier to use than the other ones (startd from scam, and ntstart from ksu). --- cut here --- static char rcsid[] = "$Id: listen.c,v 1.3 1991/11/08 20:46:08 chiuk Exp $"; /* * Startup program for netrek. Listens for connections, then forks off * servers. Based on code written by Brett McCoy, but heavily modified. * * To compile, first change the defines for SERVER, LOGFILE, DEF_PORT to what * you want. Then use: * * cc -O listen.c -o listen * * 'listen' only accepts one option, '-p' to override the default port to * listen on. Use it like: 'listen -p 2592'. * * Note that descriptor 2 is duped to descriptor 1, so that stdout and * stderr go to the same file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SERVER "server" /* filename of server */ #define LOGFILE "log" /* filename of log file */ #define DEF_PORT 2592 /* port to listen on */ int listenSock; short port = DEF_PORT; char *program; char * dateTime(); void detach(); void getConnections(); void getListenSock(); void multClose(); void reaper(); void terminate(); /* * Error reporting functions ripped from my library. */ void syserr(); void warnerr(); void fatlerr(); void err(); char *lasterr(); struct hostent * gethostbyaddr(); char * inet_ntoa(); main(argc, argv) int argc; char *argv[]; { int i; for (i = 1; i < argc; i++) { if (argv[i][0] == '-') { if (strcmp(argv[i]+1, "p") == 0) { port = atoi(argv[i+1]); } else { fatlerr(1, 0, "unrecognized option: %s.", argv[i]); } } } program = argv[0]; /* let err functions know our name */ detach(); /* detach from terminal, close files, etc. */ getListenSock(); signal(SIGCHLD, reaper); signal(SIGTERM, terminate); while (1) { getConnections(); } } /*********************************************************************** * Detach process in various ways. */ void detach() { int fd, rc, mode; mode = S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR; if ((fd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, mode)) == -1) syserr(1, "detach", "couldn't open log file. [%s]", dateTime()); dup2(fd, 2); dup2(fd, 1); multClose(1, 2, -1); /* close all other file descriptors */ warnerr(0, "started at %s on port %d.", dateTime(), port); /* fork once to escape the shells job control */ if ((rc = fork()) > 0) exit(0); else if (rc < 0) syserr(1, "detach", "couldn't fork. [%s]", dateTime()); /* now detach from the controlling terminal */ if ((fd = open("/dev/tty", O_RDWR, 0)) == -1) { warnerr("detach", "couldn't open tty, assuming still okay. [%s]", dateTime()); return; } ioctl(fd, TIOCNOTTY, 0); close(fd); setsid(); /* make us a new process group/session */ } /*********************************************************************** */ void getListenSock() { struct sockaddr_in addr; if ((listenSock = socket(AF_INET, SOCK_STREAM, 0)) < 0) syserr(1, "getListenSock", "can't create listen socket. [%s]", dateTime()); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = htons(port); if (bind(listenSock, (struct sockaddr *) &addr, sizeof(addr)) < 0) syserr(1, "getListenSock", "can't bind listen socket. [%s]", dateTime()); if (listen(listenSock, 5) != 0) syserr(1, "getListenSock", "can't listen to socket. [%s]", dateTime()); } /*********************************************************************** */ void getConnections() { int len, sock, pid; struct sockaddr_in addr; struct hostent *he; char host[100]; len = sizeof(addr); while ((sock = accept(listenSock, (struct sockaddr *) &addr, &len)) < 0) { /* if we got interrupted by a dying child, just try again */ if (errno == EINTR) continue; else syserr(1, "getConnections", "accept() on listen socket failed. [%s]", dateTime()); } /* get the host name */ he = gethostbyaddr((char *) &addr.sin_addr.s_addr, sizeof(addr.sin_addr.s_addr), AF_INET); if (he != 0) strcpy(host, he->h_name); else strcpy(host, inet_ntoa((u_long) &addr.sin_addr)); warnerr(0, "connection from %s. [%s]", host, dateTime()); /* fork off a server */ if ((pid = fork()) == 0) { dup2(sock, 0); multClose(0, 1, 2, -1); /* close everything else */ if (execl(SERVER, SERVER, host, 0) != 0) syserr(1, "getConnections", "couldn't execl %s as the server. [%s]", SERVER, dateTime()); } else if (pid < 0) syserr(1, "getConnections", "can't fork. [%s]", dateTime()); close(sock); } /*********************************************************************** * Returns a string containing the date and time. String area is static * and reused. */ char * dateTime() { time_t t; char *s; time(&t); s = ctime(&t); s[24] = '\0'; /* wipe-out the newline */ return s; } /*********************************************************************** * Handler for SIGTERM. Closes and shutdowns everything. */ void terminate() { int s; fatlerr(1, "terminate", "killed. [%s]", dateTime()); /* shutdown and close everything */ for (s = getdtablesize(); s >= 0; s--) { shutdown(s, 2); close(s); } } /*********************************************************************** * Waits on zombie children. */ void reaper() { while (wait3(0, WNOHANG, 0) > 0); } /*********************************************************************** * Close all file descriptors except the ones specified in the argument list. * The list of file descriptors is terminated with -1 as the last arg. */ void multClose(va_alist) va_dcl { va_list args; int fds[100], nfds, fd, ts, i, j; /* get all descriptors to be saved into the array fds */ va_start(args); for (nfds = 0; nfds < 99; nfds++) { if ((fd = va_arg(args, int)) == -1) break; else fds[nfds] = fd; } ts = getdtablesize(); /* close all descriptors, but first check the fds array to see if this * one is an exception */ for (i = 0; i < ts; i++) { for (j = 0; j < nfds; j++) if (i == fds[j]) break; if (j == nfds) close(i); } } /*********************************************************************** * Error reporting functions taken from my library. */ extern int sys_nerr; extern char *sys_errlist[]; extern int errno; void syserr(va_alist) va_dcl { va_list args; int rc; va_start(args); rc = va_arg(args, int); err(args); if (errno < sys_nerr) fprintf(stderr, " system message: %s\n", sys_errlist[errno]); exit(rc); } void warnerr(va_alist) va_dcl { va_list args; va_start(args); err(args); } void fatlerr(va_alist) va_dcl { va_list args; int rc; va_start(args); rc = va_arg(args, int); err(args); exit(rc); } void err(args) va_list args; { char *func, *fmt; if (program != 0) fprintf(stderr, "%s", program); func = va_arg(args, char *); if (func != 0 && strcmp(func, "") != 0) fprintf(stderr, "(%s)", func); fprintf(stderr, ": "); fmt = va_arg(args, char *); vfprintf(stderr, fmt, args); fputc('\n', stderr); fflush(stderr); } char * lasterr() { if (errno < sys_nerr) return sys_errlist[errno]; else return "No message text for this error."; } ------------------------------ From: fadden@uts.amdahl.com (Andy McFadden) Newsgroups: alt.games.xtrek Subject: Re: Getting a server working: startd Date: 11 Nov 91 20:59:19 GMT In article irie@husc9.harvard.edu (Robert E. Irie) writes: >well, I got the thing (the new startd prog) to compile, but experience >similar problems to the ones I had when using startd. The prog/daemon/whatever >fails to correctly handle quits/kills- after a few sessions, I had four >"guest" logins corresponding to the fours times I tried to connect, >and killing the server on finding i couldnt quit > >I killed EVERYTHING, from the newstartd to the ntserv processes, but it >even now it still thinks there are four people playing. any suggestions? Yeah, try "nuke": #include #include #include #include #include "defs.h" main() { extern int errno; int shmemKey = PKEY; int shmid; errno = 0; shmid = shmget(shmemKey, 0, 0); if (shmid < 0) { if (errno != ENOENT) { perror("shmget"); exit(1); } shmid = shmget(shmemKey, 0, 0); if (shmid < 0) { fprintf(stderr, "Daemon not running\n"); exit (1); } } shmctl(shmid, IPC_RMID, 0); /* nuke it */ } This kills the shared memory segment. It can be left sitting around when the daemon dies abnormally. ------------------------------ From: maguire@sun.soe.clarkson.edu (Bill Maguire) Newsgroups: alt.games.xtrek Subject: Re: Robot kills server Date: 4 Dec 91 19:13:07 GMT > 2) You may have a problem with the game freezing up > when a robot joins the game. If so, you either have to: 1) remove the > signal(SIGCHLD, reaper) call in daemonII.c (if your system can survive > without it), or 2) remove all calls to check_load in daemonII.c. This > will mean that your game won't shut itself down when the load gets too > high (big deal). This works! Thanks, bill. ------------------------------ From: fadden@uts.amdahl.com (Andy McFadden) Newsgroups: alt.games.xtrek Subject: Re: scws9 server Date: 13 Dec 91 21:15:51 GMT In article irie@scws9.harvard.edu (Robert E. Irie) writes: > For some reason, the old port, 2592 dosent work. >I will look into it, but for now, please connect to scws9.harvard.edu through >port 20592. Thanks Ports start filling up from about 1024, but every once in a while they'll work their way up to 2592. Try doing a "netstat | grep 2592" and see if somebody is sitting on it. Better to start with a nice high number anyway (20592 is good). ------------------------------ From: rjc@cstr.ed.ac.uk (Richard Caley) Newsgroups: alt.games.xtrek Subject: Runaway robots Date: 17 Dec 91 11:10:36 GMT [Warning boring computer programming details follow]. I have been having fun getting the server code up and running. I snarfed the sorce from scam.Berkeley.EDU, applied the patches from the archive and all seemed more or less well. Except... When a robot died I was getting a runaway robotII process. I tracked it down to the following lines in rmove.c while (me->p_status == PEXPLODE) ; Now. three guesses what a decent optimiser does to that code? The optimiser in my case was gcc. The solution is to tell it that the structures in the shared memory segment are, in the words of Henry Spencer, connected to the twilight zone. Shove `volatile' (well, __volatile__ since it wants -traditional) in front of the structure pointer declarations for the shared structures (me, players, messages etc) in data.[ch]. You then need to go through the code making lots of temporary pointers volatile too. Hope this saves someone some puzzlement. Now if I can find out what is stepping on the stack in x11netrek, all will be well. ------------------------------ From: fadden@uts.amdahl.com (Andy McFadden) Newsgroups: alt.games.xtrek Subject: Debugging Your Netrek Server Date: 21 Dec 91 06:18:50 GMT (let's discuss this & then archive it with the server stuff) Simple rules of thumb for debugging your netrek server: - if your client says that it got a read error (received 0 bytes - EOF hit), then the ntserv process associated with your client has died. You need to debug that. - if you can start everything up just fine, but nothing seems to work (it looks like it's updating, and you can raise and lower shields, but you can't fire torps and your phaser shots just hang), then the daemonII process has died. - if the client dies, your ntserv process will start spitting out junk. This is usually pretty obvious. - if robots are causing a problem, see the README files that came with the game. To debug an ntserv process, do the following: netrek -s 2345 & (any number will do) gdb ntserv (use your favorite debugger) run -s 2345 (use the name of the machine that you just ran the "netrek" process on) When ntserv dies, your debugger will tell you where and why. For best results, compile everything with "-g" until you are sure it all works. To debug a daemonII process, kill any existing daemon. Note that the shared memory segment may still be around; you may have to remove it manually (see "nuke.c" below, or use a command that comes with your OS and kill segment 128). gdb daemonII run After 60 seconds of being idle, daemonII will exit. Start up your netrek and ntserv processes now. To debug a robot, just gdb robotII run (yes Virginia, you CAN run robots from the command line.) Instructions for solving a given problem may be contained in other server help files. Please consult them before posting queries to the net. If you're unable to figure out what is going wrong, please include as much information as you possibly can when you post (no, don't send a uuencoded core dump, just give lots of details about your machine type, OS version, general flavor (BSD or SYSV), version of X (10 or 11), etc). ------------------------------ To: jch@cs.cmu.edu Subject: Netrek FTP Archive - server-patches Date: Fri, 17 Jan 92 15:42:58 EST From: Laurajean Neafsey In the server-patches file, there is code for "nuke" which cleans up the shared memory segment when the daemon dies abnormally (submitted by Andy McFadden) You may want to add a comment that for non-System V environments (like Sparc-Sun OS), you will need to add an include for or the equivalent for the typedef of ushort, and key_t. ------------------------------ Date: Thu, 6 Feb 92 17:11 PST From: fadden@uts.amdahl.com (Andy McFadden) To: jch@cs.cmu.edu Subject: For the archive... visible tractors I decided to update my server to allow visible tractor beams. Here's a brief description of what I did (takes about six lines of changes to transmit p_tractor... MUCH easier than I thought). Kindly archive this somewhere. The actual tractor display code is from xsg; the only thing that has changedx is that I put in a clause so that you only see tractor beams that you originate. I think that it's a Good Thing not to be able to see other's beams. You could make an argument for being able to see them when you're the target, but I think that takes some of the fun out of it (more like a borg feature than a standard client feature). - Andy ===== Sorry, I can't just send a diff because I hosed the originals. I included a few lines of the original source to make it clear where the mods should be made. The lines that must be added are marked with my initials. ===== In ntserv/packets.h (usually linked with netrek/packets.h): struct flags_spacket { char type; /* SP_FLAGS */ char pnum; /* whose flags are they? */ char tractor; /* ATM - visible tractors (was pad1) */ char pad2; unsigned flags; }; struct you_spacket { char type; /* SP_YOU */ char pnum; /* Guy needs to know this... */ char hostile; char swar; char armies; char tractor; /* ATM - visible tractor (was pad1) */ char pad2; char pad3; [...] ===== In ntserv/socket.c: In updateSelf(): clientSelf.whodead=htons(me->p_whodead); clientSelf.damage=htonl(me->p_damage); clientSelf.tractor=(char)me->p_tractor; /* ATM - visible tractor */ sendClientPacket(&clientSelf); In updateShips(): if (me!=pl && flags->flags != htonl(FLAGMASK & pl->p_flags)) { flags->type=SP_FLAGS; flags->pnum=i; flags->flags=htonl(FLAGMASK & pl->p_flags); flags->tractor = (char) pl->p_tractor; /* ATM - visible tractor */ sendClientPacket(flags); ===== In netrek/socket.c: In handleSelf(): me->p_whydead = ntohs(packet->whydead); me->p_whodead = ntohs(packet->whodead); me->p_tractor = (short) packet->tractor; /* ATM - visible tractors */ In handleFlags(): players[packet->pnum].p_flags=ntohl(packet->flags); players[packet->pnum].p_tractor=(short) packet->tractor; /* ATM - visible tractors */ redrawPlayer[packet->pnum]=1; That's all you need to update p_tractor (about six lines total). To actually display the beams, you need to insert something like the following into redraw.c (I actually pulled this out of xsg...) /* Now draw his phaser (if it exists) */ php = &phasers[j->p_no]; if (php->ph_status != PHFREE) { [...] } /* ATM - visible tractor/pressor beams */ /* (for the standard client, I am allowing them for self ONLY) */ if (j == me && (j->p_flags & PFTRACT || j->p_flags & PFPRESS)) { double theta; unsigned char dir; int lx[2], ly[2]; tx = (players[j->p_tractor].p_x - me->p_x) / SCALE + WINSIDE / 2; ty = (players[j->p_tractor].p_y - me->p_y) / SCALE + WINSIDE / 2; if (tx == dx && ty == dy) continue; /* this had better be last in for(..) */ #define XPI 3.1415926 theta = atan2((double) (tx - dx), (double) (dy - ty)) + XPI / 2.0; dir = (unsigned char) (theta / XPI * 128.0); lx[0] = tx + (Cos[dir] * (shield_width/2)); ly[0] = ty + (Sin[dir] * (shield_width/2)); lx[1] = tx - (Cos[dir] * (shield_width/2)); ly[1] = ty - (Sin[dir] * (shield_width/2)); #undef XPI W_MakeLine(w, dx, dy, lx[0], ly[0], foreColor); W_MakeLine(w, dx, dy, lx[1], ly[1], foreColor); clearline[0][clearlcount] = dx; clearline[1][clearlcount] = dy; clearline[2][clearlcount] = lx[0]; clearline[3][clearlcount] = ly[0]; clearlcount++; clearline[0][clearlcount] = dx; clearline[1][clearlcount] = dy; clearline[2][clearlcount] = lx[1]; clearline[3][clearlcount] = ly[1]; clearlcount++; } ------------------------------ From: leonard@mimsy.umd.edu (Leonard Dickens) Newsgroups: alt.games.xtrek Subject: Server bombing patch Date: 18 Feb 92 21:04:20 GMT In the Next Big Release it would be nice to twiddle the bombing code so that AS can bomb just one army, as I believe was originally intended. Here is a patch: In daemonII.c, replace lines 1520-1582: rnd = random() % 100; if (rnd < 50) { continue; } else if (rnd < 80) { ... else { j->p_stats.st_armsbomb += x ; } /* Send in a robot if there are no other defenders and the planet is in the team's home space */ With this: /*============================= snip snip ================================*/ rnd = random() % 10 ; switch (rnd) { case 0: case 1: case 2: case 3: h = 0 ; break ; case 4: h = (j->p_ship.s_type == ASSAULT) ? 4 : 0 ; break ; case 5: case 6: h = 1 ; break ; case 7: h = (j->p_ship.s_type == ASSAULT) ? 2 : 1 ; break ; case 8: h = 2 ; break ; case 9: h = 3 ; break ; } if (h == 0) continue ; l->pl_armies -= h ; if (l->pl_armies < 5) l->pl_flags |= PLREDRAW; j->p_kills += 0.02 * h ; j->p_armsbomb += h ; j->p_genoarmsbomb += h ; checkmaxkills(i); /* Give him bombing stats if he is bombing a team with 3+ */ if (status->tourn && (realNumShips(l->pl_owner) >= 3)) { status->armsbomb += h ; j->p_stats.st_tarmsbomb += h ; } else { j->p_stats.st_armsbomb += h ; } /* Send in a robot if there are no other defenders and the planet is in the team's home space */ /*============================= snip snip ================================*/ Compared to the way things currently work, this code changes very little. The bombing rate per time tick stays the same for all ship types, namely 1.3 armies/tick for AS, and 0.8 armies/tick otherwise. AS have a chance to bomb only one army; this will degrade their performance somewhat against 5-army planets. On the other hand, AS bomb rates are smoother (lower standard deviation from norm) with this implementation, getting some effect on 20% more time ticks than before. -Leonard ------------------------------ From: kolb@crd.ge.com (Mark A. Kolb) Newsgroups: alt.games.xtrek Subject: Minor Bug in Bronco Sources Date: 28 Feb 92 19:51:52 GMT The recent general release of the Bronco server source (thank you, Terence!) coincided nicely with my need to move our local server to another machine, so I took the plunge... We had our first test run today, and noticed a minor problem with the robots, specifically Terminators: they tended to hang around the center of the galaxy not doing anything until someone wandered close. I finally tracked it down to a minor bug in 'rmove.c', where the robot's speed is to be set based on it's distance from the target. In the distribution, line 624 reads: set_speed((hypot(dx, dy) / 5000) + 3); Since 'set_speed' is expecting an integer, though, a cast on the result of 'hypot' is needed: set_speed(( ((int) hypot(dx, dy)) / 5000) + 3); With this fix added, the Terminator's strategy is much improved... ------------------------------ Date: Mon, 16 Mar 92 10:39 PST From: fadden@uts.amdahl.com (Andy McFadden) To: rjones@dsd.es.com Subject: Restartable daemon Cc: jch@cs.cmu.edu Given the flack over the recent crashed USC game, here's the restartable daemonII.c (which you probably ought to add to the INL server). The restart stuff is all in main(). I think. It's been a while. Basically, if you invoke daemonII on its own with -R, it won't kill the existing shared memory segment, so everything will pick up right where it left off. The nice thing about the daemon is that 99 44/100% of the state is in the shared memory segment or the ntserv processes... (BTW, I don't know why I removed all the log fprintf()s. Must've been a reason back when I initially ported the thing. You'll also have to step around some of the SystemV-isms, like the modified system calls. But, this'll show you what needs to be done.) Cc:ed to jch for inclusion in the server-mods file. Might be nice for somebody to replace this with a set of patches for bronco source or whatever, so that future server ops won't have to do so much splicing. [ patches follow ] ------------------------------ From: rjones@itchy.dsd.es.com (Ray Jones - Perp) Newsgroups: alt.games.xtrek Subject: Restartable daemon Date: 16 Mar 92 20:58:55 GMT Andy McFadden sent me the changes that allow the daemon to be run with a -R option to have it connect up to the shared memory, rather than killing and resetting it. I put them into the bronco code, and will put them into the INL server next season as well. I'll tack them onto this message, they aren't that long. They can probably be applied to the INL code as well. if not, someone tell me, and I'll put them into it by hand, then get diffs for that. *** daemonII.c Mon Mar 16 13:51:32 1992 --- /tst/rjones/INL/bronco-changes/daemonII.c Wed Feb 12 20:56:29 1992 *************** *** 48,55 **** static int tcount[MAXTEAM + 1]; unsigned char getbearing(); - static int Restart = 0; /* ATM */ - main(argc, argv) int argc; char **argv; --- 48,53 ---- *************** *** 62,76 **** long move(); long reaper(); long freemem(); ! if (argc > 2) /* ATM: was > 1 */ debug = 1; - if (argc > 1 && !strcmp(argv[1], "-R")) { - Restart = 1; - } else { - Restart = 0; - } - fprintf(stderr,"Daemon says 'hello!'\n"); srandom(getpid()); readsysdefaults(); --- 60,68 ---- long move(); long reaper(); long freemem(); ! if (argc > 1) debug = 1; fprintf(stderr,"Daemon says 'hello!'\n"); srandom(getpid()); readsysdefaults(); *************** *** 80,98 **** } /* detach();*/ } - if (!Restart){ - /* Kill any existing segments */ - - if ((shmid = shmget(shmemKey, 0, 0)) >= 0) { - fprintf(stderr, "Killing existing segment\n"); - shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0); - } ! shmid = shmget(shmemKey, sizeof(struct memory), IPC_CREAT | 0777); ! } else { ! shmid = shmget(shmemKey, 0, 0); } if (shmid < 0) { perror("can't open shared memory"); exit (1); --- 72,86 ---- } /* detach();*/ } ! /* Kill any existing segments */ ! ! if ((shmid = shmget(shmemKey, 0, 0)) >= 0) { ! fprintf(stderr, "Killing existing segment\n"); ! shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0); } + shmid = shmget(shmemKey, sizeof(struct memory), IPC_CREAT | 0777); if (shmid < 0) { perror("can't open shared memory"); exit (1); *************** *** 118,180 **** messages = sharedMemory->messages; teams = sharedMemory->teams; ! if (!Restart) { ! for (i = 0; i <= MAXTEAM; i++) { ! teams[i].s_turns = 0; ! teams[i].s_surrender = 0; ! } ! ! for (i = 0; i < MAXPLAYER; i++) { ! players[i].p_status = PFREE; ! players[i].p_stats.st_tticks=1; ! players[i].p_no=i; ! } ! ! plfd = open(PLFILE, O_RDWR, 0744); ! if (plfd < 0) { ! fprintf(stderr, "No planet file. Restarting galaxy\n"); doResources(); } ! else { ! if (read(plfd, (char *) planets, sizeof(pdata)) != sizeof(pdata)) { ! fprintf(stderr, "Planet file wrong size. Restarting galaxy\n"); ! doResources(); ! } ! } #ifdef PMOVEMENT ! pinit(); #endif ! ! glfd = open(GLOBAL, O_RDWR, 0744); ! if (glfd < 0) { ! fprintf(stderr, "No global file. Resetting all stats\n"); ! } else { ! if (read(glfd, (char *) status, sizeof(struct status)) != ! sizeof(struct status)) { ! fprintf(stderr, "Global file wrong size. Resetting all stats\n"); ! bzero((char *) status, sizeof(struct status)); ! } } - if (status->time==0) { - /* Start all stats at 1 to prevent overflow */ - status->time=1; - status->timeprod=1; - status->planets=1; - status->armsbomb=1; - status->kills=1; - status->losses=1; - } - - status->active = 0; - status->wait = 0; - status->count = 0; - status->request = 0; - status->gameup = 1; - } else { /* if (!Restart) */ - plfd = open(PLFILE, O_RDWR, 0744); - glfd = open(GLOBAL, O_RDWR, 0744); - status->gameup = 1; } (void) signal(SIGCHLD, reaper); --- 106,162 ---- messages = sharedMemory->messages; teams = sharedMemory->teams; ! for (i = 0; i <= MAXTEAM; i++) { ! teams[i].s_turns = 0; ! teams[i].s_surrender = 0; ! } ! ! for (i = 0; i < MAXPLAYER; i++) { ! players[i].p_status = PFREE; ! players[i].p_stats.st_tticks=1; ! players[i].p_no=i; ! } ! ! plfd = open(PLFILE, O_RDWR, 0744); ! if (plfd < 0) { ! fprintf(stderr, "No planet file. Restarting galaxy\n"); ! doResources(); ! } ! else { ! if (read(plfd, (char *) planets, sizeof(pdata)) != sizeof(pdata)) { ! fprintf(stderr, "Planet file wrong size. Restarting galaxy\n"); doResources(); } ! } #ifdef PMOVEMENT ! pinit(); #endif ! ! glfd = open(GLOBAL, O_RDWR, 0744); ! if (glfd < 0) { ! fprintf(stderr, "No global file. Resetting all stats\n"); ! } else { ! if (read(glfd, (char *) status, sizeof(struct status)) != ! sizeof(struct status)) { ! fprintf(stderr, "Global file wrong size. Resetting all stats\n"); ! bzero((char *) status, sizeof(struct status)); } } + if (status->time==0) { + /* Start all stats at 1 to prevent overflow */ + status->time=1; + status->timeprod=1; + status->planets=1; + status->armsbomb=1; + status->kills=1; + status->losses=1; + } + + status->active = 0; + status->wait = 0; + status->count = 0; + status->request = 0; + status->gameup = 1; (void) signal(SIGCHLD, reaper); ------------------------------ From: rjc@cstr.ed.ac.uk (Richard Caley) Newsgroups: rec.games.netrek,alt.games.xtrek Re: Really strange server fix Date: Mon Apr 06 12:29:08 EDT 1992 In article <1992Apr4.215754@IASTATE.EDU>, Bradley C Karsjens (bck) writes: bck> [robots loop when dead] This is caused by your c compiler (probably and ansi one, if not it's a bug) not knowing that the variable being tested may change without the program doing anything. The solution is either to get your compiler to treat all globals as subject to unexpected change (-fvolatile works for gcc) or add volatile declarations to the code. There is a patch for the bronco source to do the latter in jch's archive on gs69.sp.cs.cmu.edu. ------------------------------ Newsgroups: rec.games.netrek From: rwd@CS.CMU.EDU (Randall Dean) Subject: Re: Compiling Netrek.SRC.tar.Z on a MACH 386 (really 486) system Date: Fri, 24 Apr 92 17:19:57 GMT Running on MACH is no problem. You need to have a name server running, either snames or netmsgserver, and you need the netmemoryserver running. The netmemoryserver will supply the shared memory instead of sysVshm. At the end of this message are parts of my daemonII.c and my enter.c. You will need to make similar changes to openmem() in any of the utilities like planets, players, etc /* daemonII.c */ #include "copyright.h" #include #include #include #ifndef MACH #include #include #endif MACH #include #include #include #include #include "defs.h" #include "struct.h" #include "data.h" #include "planets.h" #include #include #include #define fuse(X) ((ticks % (X)) == 0) #define TOURNEXTENSION 15 /* Tmode gone for 15 seconds 8/26/91 TC */ #define NotTmode(X) (!(status->tourn) && ((X - tourntimestamp)/10 > TOURNEXTENSION)) /* Run the game */ long random(); long lseek(); void doResources(); static struct itimerval udt; #ifndef MACH static int shmid; #endif MACH static int debug = 0; static int ticks = 0; static int tourntimestamp = 0; /* ticks since last Tmode 8/2/91 TC */ static int plfd; static int glfd; jmp_buf env; char *teamNames[9] = {"", "Federation", "Romulans", "", "Klingons", "", "", "", "Orions"}; char *teamVerbage[9] = {"", "has", "have", "", "have", "", "", "", "have"}; static int tcount[MAXTEAM + 1]; unsigned char getbearing(); static struct memory *sharedMemory; main(argc, argv) int argc; char **argv; { register int i; #ifndef MACH int shmemKey = PKEY; struct shmid_ds smbuf; #endif MACH int x = 0, k=0; int move(); int reaper(); int freemem(); if (argc > 1) debug = 1; fprintf(stderr,"Daemon says 'hello!'\n"); srandom(getpid()); readsysdefaults(); if (!debug) { for (i = 0; i < NSIG; i++) { (void) signal(i, freemem); } /* detach();*/ } /* Kill any existing segments */ #ifndef MACH if ((shmid = shmget(shmemKey, 0, 0)) >= 0) { fprintf(stderr, "Killing existing segment\n"); shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0); } shmid = shmget(shmemKey, sizeof(struct memory), IPC_CREAT | 0777); if (shmid < 0) { perror("can't open shared memory"); exit (1); } /* Hose Ed's robots */ shmctl(shmid, IPC_STAT, &smbuf); smbuf.shm_perm.uid = geteuid(); smbuf.shm_perm.mode = 0700; shmctl(shmid, IPC_SET, &smbuf); sharedMemory = (struct memory *) shmat(shmid, 0, 0); #else { #include #include #include extern port_t name_server_port; port_t netmemory_port; port_t obj, mem_obj, mem_ctl; kern_return_t ret; ret = netname_look_up(name_server_port, "", "netmemoryserver", &netmemory_port); if (ret != KERN_SUCCESS) { mach_error("getting netmemoryserver port",ret); exit(1); } ret = netmemory_create(netmemory_port, sizeof(struct memory), &obj, &mem_ctl); if (ret != NETMEMORY_SUCCESS) { mach_error("netmemory_create",ret); exit(1); } ret = netmemory_cache(netmemory_port, obj, &mem_obj); if (ret != NETMEMORY_SUCCESS) { mach_error("netmemory_cache", ret); (void) netmemory_destroy(mem_ctl); exit(1); } ret = netname_check_in(name_server_port, "netrek", task_self(), mem_obj); if (ret != NETNAME_SUCCESS) { mach_error("Checkin In netrek port",ret); exit(1); } ret = vm_map(task_self(), &sharedMemory, sizeof(struct memory), 0, 1, mem_obj, 0, 0, VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE); if (ret != KERN_SUCCESS) { mach_error("vm_map", ret); (void) netmemory_destroy(mem_ctl); exit(1); } } #endif if (sharedMemory == (struct memory *) -1) { perror("shm attach"); exit (1); } players = sharedMemory->players; torps = sharedMemory->torps; plasmatorps = sharedMemory->plasmatorps; status = sharedMemory->status; planets = sharedMemory->planets; phasers = sharedMemory->phasers; mctl = sharedMemory->mctl; messages = sharedMemory->messages; teams = sharedMemory->teams; #ifdef THRONEWORLD sharedMemory->king = -1; #endif THRONEWORLD for (i = 0; i <= MAXTEAM; i++) { teams[i].s_turns = 0; teams[i].s_surrender = 0; } for (i = 0; i < MAXPLAYER; i++) { players[i].p_status = PFREE; players[i].p_stats.st_tticks=1; players[i].p_no=i; } plfd = open(PLFILE, O_RDWR, 0744); if (plfd < 0) { fprintf(stderr, "No planet file. Restarting galaxy\n"); doResources(); } else { if (read(plfd, (char *) planets, sizeof(pdata)) != sizeof(pdata)) { fprintf(stderr, "Planet file wrong size. Restarting galaxy\n"); doResources(); } } glfd = open(GLOBAL, O_RDWR, 0744); if (glfd < 0) { fprintf(stderr, "No global file. Resetting all stats\n"); } else { if (read(glfd, (char *) status, sizeof(struct status)) != sizeof(struct status)) { fprintf(stderr, "Global file wrong size. Resetting all stats\n"); bzero((char *) status, sizeof(struct status)); } } if (status->time==0) { /* Start all stats at 1 to prevent overflow */ status->time=1; status->timeprod=1; status->planets=1; status->armsbomb=1; status->kills=1; status->losses=1; } status->active = 0; status->wait = 0; status->count = 0; status->request = 0; status->gameup = 1; (void) signal(SIGCHLD, reaper); (void) signal(SIGALRM, move); udt.it_interval.tv_sec = 0; udt.it_interval.tv_usec = UPDATE; udt.it_value.tv_sec = 0; udt.it_value.tv_usec = UPDATE; (void) setitimer(ITIMER_REAL, &udt, (struct itimerval *) 0); (void) setjmp(env); check_load(); while (1) { pause(); if (debug) { if (!(++x % 50)) printf("Mark %d\n", x); } } } freemem(sig, code, scp) int sig, code; struct sigcontext *scp; { register int i; register struct player *j; FILE *logfile; long timeoday; if (sig == SIGFPE) return; /* Too many errors occur ... ignore them. */ /* Blow players out of the game */ for (i = 0, j = &players[i]; i < MAXPLAYER; i++, j++) { j->p_status = PEXPLODE; j->p_whydead = KDAEMON; j->p_ntorp = 0; j->p_nplasmatorp = 0; j->p_explode = 600/PLAYERFUSE; /* ghost buster was leaving players in */ } /* Kill waiting players */ status->count=0; save_planets(); sleep(2); #ifndef MACH shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0); #endif MACH exit(0); } /* * enter.c */ #include "copyright.h" #include #include #ifndef MACH #include #include #endif MACH #include #include #include #include #include /* 7/16/91 TC */ #include "defs.h" #include "struct.h" #include "data.h" openmem() { extern int errno; #ifndef MACH int shmemKey = PKEY; int shmid; #endif MACH struct memory *sharedMemory; errno = 0; #ifndef MACH shmid = shmget(shmemKey, 0, 0); if (shmid < 0) { if (errno != ENOENT) { perror("shmget"); exit(1); } startdaemon(); sleep(2); shmid = shmget(shmemKey, 0, 0); if (shmid < 0) { fprintf(stderr, "Daemon not running (err:%d)\n",errno); exit (1); } } sharedMemory = (struct memory *) shmat(shmid, 0, 0); #else { #include extern port_t name_server_port; port_t netmemory_port; port_t obj; kern_return_t ret; ret = netname_look_up(name_server_port, "", "netmemoryserver", &netmemory_port); if (ret != KERN_SUCCESS) { mach_error("getting netmemoryserver port",ret); exit(1); } ret = netname_look_up(name_server_port, "", "netrek", &obj); if (ret != KERN_SUCCESS) { startdaemon(); sleep(2); ret = netname_look_up(name_server_port, "", "netrek", &obj); if (ret != KERN_SUCCESS) { mach_error("getting netrek port",ret); fprintf(stderr, "Daemon not running\n"); exit (1); } } ret = vm_map(task_self(), &sharedMemory, sizeof(struct memory), 0, 1, obj, 0, 0, VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_SHARE); if (ret != KERN_SUCCESS) { mach_error("vm_map", ret); exit(1); } } #endif if (sharedMemory == (struct memory *) -1) { perror("shared memory"); exit (1); } players = sharedMemory->players; torps = sharedMemory->torps; plasmatorps = sharedMemory->plasmatorps; status = sharedMemory->status; planets = sharedMemory->planets; phasers = sharedMemory->phasers; mctl = sharedMemory->mctl; messages = sharedMemory->messages; teams = sharedMemory->teams; } ------------------------------ From: hadley@vlsi.ics.uci.edu (Tedd Hadley) Newsgroups: alt.games.xtrek Subject: Re: Tmode scumming Summary: server mods Date: 20 Jan 92 04:52:16 GMT In <1992Jan19.195352.130821@cs.cmu.edu> rwd+@CS.CMU.EDU (Randall Dean) writes: >That is my name for people logging on mutliple times to cause >tmode. This is a totally contemptable action. This is the only >warning I will give publically about it happenning on rwd4. If >I see it happen (or it is reported to me and my logs verify it), >you are gone and so are your characters. Period. I advise people >to not let their friend play from the same machine at the same time >under the same account name either, since I will probably add code >to detect Tmode scumming and this will be hard to distinguish from >the former circumstance. Code I hacked up for the above situation some months ago: In ntserv/main.c : main() /* multiple login check */ if(no_multlogin && !login_ok(me->p_login)){ me->p_explode = 10; me->p_whydead = KQUIT; me->p_status = PEXPLODE; chaos = 0; /* just in case */ warning("Someone with your login is already playing."); } input(); } login_ok(name) char *name; { register i; struct player *p; for(i=0, p=players; i< MAXPLAYER; i++,p++){ if(p->p_status != PFREE && p != me){ if(strcmp(p->p_login, me->p_login) == 0) return 0; } } return 1; } I made the above a configurable option by adding 'no_multlogin' as a global variable (defs.c, defs.h) which is then set in sysdefaults.c by the 'NOMULTLOGIN=1' in .sysdef ++ Tedd Hadley (hadley@ics.uci.edu) ------------------------------ From: jesup@cbmvax.commodore.com (Randell Jesup) Newsgroups: rec.games.netrek Subject: xtkill bug Date: 31 May 92 00:58:36 GMT I found a bug in xtkill while trying to nuke Iggy in our last game here. player= atoi(argv[1]); should be something like: player= (isdigit(argv[1][0]) ? atoi(argv[1]) : tolower(argv[1][0])-'a' + 10); if (player >= MAXPLAYER) print error and exit I kept killing poor O0 When I was trying to kill Ij. ------------------------------ Newsgroups: rec.games.netrek From: terence@bronco.ece.cmu.edu (Terence Chang) Subject: Long standing bug in detonation code Date: Tue, 23 Jun 1992 01:36:15 GMT The scam sources of Feb. 7, 1991, and hence all bronco-based sources, contain a bug in ntserv/detonate.c. Because of a missing pair of braces, the server uses a square detonation radius rather than circular. Because torp damage is distance-based, torps detted at a "diagonal" wind up doing significantly less damage (zero if outside of the circular radius). Fix: Change ntserv/detonate.c, lines 54-57 from: if (dx * dx + dy * dy < DETDIST * DETDIST) j->t_whodet = me->p_no; j->t_status = TDET; to something like: if (dx * dx + dy * dy < DETDIST * DETDIST) { /* bug fix: braces were missing, resulting in a square det radius 6/22/92 TC */ j->t_whodet = me->p_no; j->t_status = TDET; } Terence ------------------------------ Newsgroups: rec.games.netrek Subject: Long standing bug in orbit code From: terence@bronco.ece.cmu.edu (Terence Chang) Date: Tue, 23 Jun 1992 19:14:08 GMT There is another long standing bug in the Feb. 7, 1991, scam sources and all bronco-based sources. If an orbiting player sends a very rapid stream of orbit requests (e.g., autorepeating 'o' key), the server will constantly "re-hook" the player onto the planet. This re-hook results in additional planet rotation beyond standard orbit. Depending on the player's position in orbit, the ship will either remain stationary, move backwards (counterclockwise) slowly, or move forwards (clockwise) rapidly: 0 <-- at 0, ship is stationary | 270 --+-- 90 | <-- up to ~135, ship moves backwards to 0 180 <-- beyond 135, ship moves forwards rapidly to 0 This bug can be exploited while bombing/beaming -- those operations continue through successive orbit requests. Fix: ignore orbit requests for orbiting players. Change ntserv/orbit.c, lines 27-35, from: orbit() { register int i; register struct planet *l; unsigned char dir; int dx, dy; if (me->p_speed > ORBSPEED ) { [yes, two blank newlines already in the right place] to something like: orbit() { register int i; register struct planet *l; unsigned char dir; int dx, dy; if (me->p_flags & PFORBIT) return; /* bug fix: no reverse/rapid orbiting 6/23/92 TMC */ if (me->p_speed > ORBSPEED ) { Terence PS: You really don't want a square detting radius in your server. Any torps detted within the square but outside the circle will do negligible damage. The scam/bronco sources use: #define DAMDIST 2000 /* At this range a torp does damage */ #define DETDIST 1600 /* At this range a torp can be detonated */ If you work out the geometry, you'll see that DETDIST could be enlarged to compensate, but it's a matter of admininstrator taste. Terence ------------------------------ From: hadley@vlsi.ics.uci.edu (Tedd Hadley) Subject: Multiprocessors and shared memory bug Newsgroups: rec.games.netrek Date: 22 Jul 92 07:39:36 GMT This should go to reg.games.netrek.programmer, but since that group doesn't exist... :) A recent problem on the calvin.usc.edu server got me looking into shared memory in a little more detail and I believe I've discovered a potential problem in daemonII.c for multiprocessor machines. (If Randall Dean has already mentioned this and I've missed it, apologies in advance). Calvin was recently upgraded to a sun 4/690 with 4 cpus, and the other day Dan found two daemon processes running at the same time, resulting in one heckuva fast game. Most vanilla server code (scam & ksu, at least) does the following: /* (1) Kill any existing segments */ if ((shmid = shmget(shmemKey, 0, 0)) >= 0) { fprintf(stderr, "Killing existing segment\n"); fflush(stderr); shmctl(shmid, IPC_RMID, (struct shmid_ds *) 0); } /* (2) Create a new segment */ shmid = shmget(shmemKey, sizeof(struct memory), IPC_CREAT | 0700); if (shmid < 0) { perror("can't open shared memory"); exit (1); } So what happens when two or more clients connect at the same time when the daemon isn't running? Each ntserv starts a daemonII process and usually one gets to the shmget IPC_CREAT before the other, the second then removes the shared memory segment and creates its own leaving the first process to eventually die from lack of players (or is it segmentation violation -- not sure which but it does die soon). No problems otherwise, the second process continues and the game goes on.. Can both processes reach the shmget IPC_CREAT at the same time? I don't think this is likely on most uniprocessor systems due to the length of the cpu time slice, but on multiprocessor systems it's certainly possible. Now for the bug: if both processes do reach the create at the same time _neither_ will fail. The IPC_CREAT flag only tells the system to create the segment if it doesn't exist, it says nothing about failing if the segment already exists. What happens then is you have two daemon processes sharing the same shmem segment, resulting in a hyperspeed game like the one experienced on calvin last week. The solution is simple. Add IPC_EXCL to the second shmget: shmid = shmget(shmemKey, sizeof(struct memory), IPC_CREAT | IPC_EXCL | 0700); Now, regardless of how many processes get to this statement at the same time, only one will succeed (enforced by indivisible kernel operations I would hope) and later processes will fail with EEXIST. However this does mean that if a shared memory segment exists due to a crashed or kill -9'd daemon then you'll have to remove the segment by hand ('ipcrm' or 'nuke' if you have it). ++ Tedd Hadley (hadley@ics.uci.edu) ------------------------------ From: mehlhaff@ocf.berkeley.edu (ERic Mehlhaff) Newsgroups: rec.games.netrek Subject: Announcing Mynetd, a much better tool for server connection handling Date: 3 Aug 92 11:18:23 GMT I just finished a tool I wrote to start netrek servers up from. If you're still running your netrek server from ntstart, startd, or any of startd's children, or even if you're running it from inetd, you might want to take a look at this program. What does mynetd do? Well, basically, it runs just like inetd (In fact, there are similarities in the source, but I found them out after I looked at inetd.c while debugging!). Mynetd is much simpler to use, however, and it has some features that make it easier to start ntserv processes from ( nor more silly programs that just figgure out hostnames, dump to a logfile, and exec ntserv!). All this and it doesn't have to be run by the superuser, as inetd does. For example, a line in the Mynetd configuration file to do a normal netrek startup might look like: 2592 1 2 /usr/games/netrek/ntserv %h And you could have several ports listening with slightly options. IT is ideal, for example, for INL servers as well. AND, since it's such a flexible program, there's a variety of other things that could be started from it. But I'm wasting bandwidth here, especially with all the documentation files and examples that I wrote for it. If you're interested, take a look at it! THe complete mynetd distribution can be ftp'd from the OCF's ftp site (ocf.berkeley.EDU in pub/netrek/Mynetd.shar), or I can mail it to you if you're not actually on the internet. Now that this is done, I can go back and fix/finish METASERVER! ERic mehlhaff, mehlhaff@ocf.Berkeley.EDU Netrek hacker "Now if only I could be paid to do this!" ------------------------------ From: bharat@shadow.Eng.Sun.COM (Bharat Mediratta) Newsgroups: rec.games.netrek Subject: System defaults bug/fix Date: 8 Sep 92 04:02:58 GMT Here's a bug w/ fix in the bronco.final server release. I'm running on a Sunergy machine under 4.1.3. I like to edit the .sysdef file with vi while the server is running, in order to change some of the defaults on the fly. I noticed that when you save the file, the daemon immediately picks up on that fact and can, in fact, re-read the defaults before (apparently) they are fully saved. So you wind up with incorrect system default settings. I modified my server to re-read the defaults file after about half a second (?) to make sure that the defaults are read in properly. I've hacked my daemonII.c file up a lot, so I can't give a good diff, but I'll fake one...the +'s represent lines that have been added: -Bharat daemonII.c: -- + #define REREAD 5 move() { . . . + static int defaultsUpdate = -1; if (++ticks == dietime) {/* no player for 1 minute. kill self */ . . . } + if ((defaultsUpdate > 0) && (ticks-defaultsUpdate) > REREAD) + { + readsysdefaults(); + defaultsUpdate=-1; + } if (update_sys_defaults()) { . . . pmessage("Loading new server configuration.", 0, MALL, "GOD->ALL"); + defaultsUpdate = ticks; } -- ------------------------------ From: hadley@codes.ics.uci.edu (Tedd Hadley) Subject: Server bug: starbases and declare_war() Newsgroups: rec.games.netrek Date: 1 Sep 92 22:52:33 GMT If a starbase uses the war window (declare war or peace) while 1-3 players are docked, the server will crash with a segmentation violation (at least under SunOS). The code in question is in interface.c'declare_war(). } else if (me->p_ship.s_type == STARBASE) { if (me->p_docked > 0) { for(i=0; ip_port[i]].p_team) { players[me->p_port[i]].p_flags &= ~PFDOCK; me->p_docked--; me->p_port[i] = VACANT; } } } } At the line marked with '*', me->p_port[i] will equal -1 (VACANT) for any vacant starbase ports. This causes a read attempt to players[-1], at which time the os says "hey, that's not your memory space!", issues a segmentation violation and murders the process (it's a jungle in there). To fix, add the lines marked with '+' (or however else you want to do it): } else if (me->p_ship.s_type == STARBASE) { if (me->p_docked > 0) { for(i=0; ip_port[i] == VACANT) + continue; if (mask & players[me->p_port[i]].p_team) { players[me->p_port[i]].p_flags &= ~PFDOCK; me->p_docked--; me->p_port[i] = VACANT; } } } } ------------------------------ Newsgroups: rec.games.netrek Subject: Re: Help needed! Installing INL server on an rs/6000 From: sfd@soda.berkeley.edu (Scott Drellishak) Date: 29 Oct 1992 19:27:29 GMT siegl@risc.uni-linz.ac.at (Kurt Siegl) writes: )Netrek is relatively portable writen, but programers asume that Unix )is BSD which is not true for modern (SYSV) machines. ) )I've got also that kind of problems while porting the Server to the )new Sequent operating system dynix/ptx which is SYSV. ) )The "can not move" problems come from problems with the statement: ) )daemonII.c: (void) signal(SIGALRM, move); ) )It can have mainly two reasons: ) )1: SYSV signal handling )SYSV cleans the signal handler after an interupt, BSD restarts it. ) )Solution: )Add the statement: )signal(SIGALRM, move); )to the end of the move procedure for an automatic restart. )You need to do that for every signal handler. Actually, you could #define signal(_foo_) sigset(_foo_), and get the same result. )2: The daemonII process terminates. ) )If this is the case check all exit points and figure out why it terminates. Well, if there's no more signal handler for SIGALRM after the first one is received, then the program dies when it receives the signal. I ported the client and server to a sysv machine a while ago, and it wasn't too bad...the only other thing I remember being tricky was the gettimeofday/ftime thing, which you could always just throw out for testing (and allow fast torp firing). Scott ------------------------------ Newsgroups: rec.games.netrek Subject: Re: Help needed! Installing INL server on an rs/6000 From: fadden@uts.amdahl.com (Andy McFadden) Date: 30 Oct 92 00:58:33 GMT sfd@soda.berkeley.edu (Scott Drellishak) writes: >siegl@risc.uni-linz.ac.at (Kurt Siegl) writes: >)Solution: >)Add the statement: >)signal(SIGALRM, move); >)to the end of the move procedure for an automatic restart. >)You need to do that for every signal handler. > >Actually, you could #define signal(_foo_) sigset(_foo_), and get the same >result. But it's not quite the same result. If you put the second signal() in the signal handler, you are creating a possible race condition. sigset() has BSD signal characteristics and should be used. (Trust me, I tried it the first way on my server, and sometimes when the machine started to get really slow, the daemon would die.) I have the complete list of Stuff I Had to Do for UTS2.1 around here somewhere. fadden@uts.amdahl.com (Andy McFadden) ------------------------------ Newsgroups: rec.games.netrek From: rob@psy.vu.nl (Rob van Leeuwen) Subject: Installing an INL server on AIX (version 3.1, 3.2) Date: Thu, 5 Nov 1992 20:20:21 GMT Hi all. For those who are trying to install an INL server on AIX version 3.1 or 3.2, here's some information you might find worthwile. Installing INL on AIX 3.2 is pretty simple: just ftp the source from iacrs2.unibe.ch:/pub/aix/netrek. For those still running 3.1, get these source too, but be prepared to add some #include's (, ). Note that in defs.h, , , and are included. This is crucial for a normally operating server. Without these files, the code *will* compile though, but produce *bad* code. Thanks to Martin Schuetz (schuetz@iacrs2.unibe.ch) for porting INL3.0 to AIX 3.2. -- Rob van Leeuwen ------------------------------ From: hadley@cad.ics.uci.edu (Tedd Hadley) Subject: Re: berkeley client Newsgroups: rec.games.netrek Date: 6 Nov 92 04:21:53 GMT In <721007219.13977.0@unix6.andrew.cmu.edu> Rick writes: >would someone please explain to me why the berkeley client puts >"planet destroyed by" messages in the ALL board?? >The code that does that appears to ME to be dmessage.c. >but the berkeley client dmessage.c is identical to the old dmessage.c >except for the message to god stuff. So why are the planet destroyed >messages going to the ALL board?!?!? >the "planet taken over" messages still go the the team board... This is the result of incompatible servers. The bronco server code uses an extra 4 bits in the message flag field to indicate information about the message: MGENO, MCONQ, MTAKE, etc. (I don't think any clients use this information, although they certainly could). However, the message-to-god bit flag (MGOD) used in the new clients, xsg, and several servers has a conflict with the first bit of that info field. The conflict results in these client/server combination problems: all clients -> bronco-based server with message-to-god (bigdog, sickdog): Some messages end up in strange places (like declare-peace message to your own team window). bronco client -> non-bronco-based server with message-to-god No problem since the bronco client doesn't use the extra info anyway. * message-to-god client (ocf client, xsg) -> bronco-based server Client incorrectly interprets extra info flags as MGOD flag: message gets sent to ALL. A kludge fix to the client is to remove the MGOD test in dmessage(), although this means you won't see messages you send to GOD in the message window. "Correct" solutions: fix all bronco-based servers so those extra info fields don't conflict with MGOD, or redefine MGOD in clients/servers/xsg. ++ Tedd Hadley (hadley@uci.edu) ------------------------------ From: paulsen@eniac.seas.upenn.edu (Brian Paulsen) Newsgroups: rec.games.netrek Subject: 10 updates/sec for SB's!!! (server mods included) Date: 14 Jan 93 18:51:08 GMT Many people have argued that SB's could really benefit from 10 updates/ second. I've included the following mods so that your server will be able to use these changes. What happens is that the server will save the value of updates/sec that you would like to have. Then, everytime you die or refit, it checks what type of ship you are in. If you are in a SB, then you are free to take whatever value of updates you like. If you are not in a SB, then it limits you to whatever the server max is. in getentry.c in getEntry(team, stype) the last couple lines of this function should be changed from sendPickokPacket(1); flushSockBuf(); to sendPickokPacket(1); handleUpdatesReq(NULL); /* 12/21/92 - Brian Paulsen */ flushSockBuf(); in interface.c in do_refit(type) the last couple lines of this function should be changed from rdelay = me->p_updates + 50; warning("You are being transported to your new vessel .... "); to handleUpdatesReq(NULL); /* 12/21/92 - Brian Paulsen */ rdelay = me->p_updates + 50; warning("You are being transported to your new vessel .... "); in socket.c the function handleUpdatesReq() should be changed to... handleUpdatesReq(packet) struct updates_cpacket *packet; { #define MIN_DELAY 200000 /* load problems 4/3/92 TC */ struct itimerval udt; struct timeval timeout; static int desiredUpdates = MIN_DELAY; /* 12/21/92 - Brian Paulsen */ if (packet) { timerDelay=ntohl(packet->usecs); desiredUpdates = timerDelay; } else timerDelay = desiredUpdates; if ((timerDelay < MIN_DELAY) && (me->p_ship.s_type != STARBASE)) timerDelay=MIN_DELAY; if (timerDelay >= 1000000) timerDelay=999999; udt.it_interval.tv_sec = 0; udt.it_interval.tv_usec = timerDelay; udt.it_value.tv_sec = 0; udt.it_value.tv_usec = timerDelay; setitimer(ITIMER_REAL, &udt, 0); } in robotII.c, the following stub must be added... handleUpdatesReq(pointer) /* 12/21/92 - Brian Paulsen */ void *pointer; { return; } Brian ------------------------------ From: trown@ecst.csuchico.edu (Nick Trown) Newsgroups: rec.games.netrek Subject: Re: Posting tmode scums Date: 18 Jan 93 22:42:34 GMT [...] I totally agree with you. However, since I am a server god I of course want to limit the t-mode scummers on my server. To solve this problem I wrote a function a while ago that scans the player list when t-mode occurs and if a player is logged in more than twice it saves the list for me to look at latter and try to decide if it was abuse or not. I posted this a while ago but here it is again for those server gods that are interested. in defs.h add and make directory what you want: #define SCUM_FILE "/h/h/games/hp700/lib/ntserv/god.LOG" In dameonII.c: add before function move(): /* Check the player list for possible t-mode scummers. Nick Trown 12/19/92 */ void check_scummers() { int i, j; int num; FILE *fp; for (i=0; i1){ pmessage("Possible t-mode scummers have been found.",0,MALL,"GOD->AL L"); pmessage("They have been noted for god to review.",0,MALL,"GOD->ALL" ); if ((fp = fopen (SCUM_FILE,"a"))==NULL) { fprintf(stderr,"Unable to open scum file.\n"); return; } fprintf(fp,"POSSIBLE T-MODE SCUMMERS FOUND!!!\n"); for (j=0; j> check_scummers(); switch(oldmessage=(random() % 8)) { case 0: ------------------------------ Newsgroups: rec.games.netrek From: mummert+@AUK.WARP.CS.CMU.EDU (Todd Mummert) Subject: Re: Pop code compromise? Plague code? Date: Thu, 28 Jan 1993 09:59:39 GMT I have working code, probably major bug free, that chooses exponentially distributed interarrival times (arrivals follow a Poisson distribution). Granularity is at the 1 tick level, coarser granularity is not implemented. Handles both pops and plagues, doesn't require the need for another field in struct planets (though it does use deadtime which was already there). How fast is it? I'm not really sure, most of the time it does a simple check to see if *any* planet has popped...my guess is that the runtime is negligible compared to everything else. One issue which does come up is how to handle the pop sizes, currently I have the magic numbers set up to follow bronco rates, but each pop is simply of size [1-3] w/ equal probability. Now this is probably another bone of contention that will have to be worked out...but you pick a distribution for the pop sizes and I can change the magic numbers. I'll include the major pieces of code, so feel free to shoot them down. PLPOP and PLPLAGUE are new values for pl_flags (added to struct.h) the rest of the changes are solely in daemonII.c Besides the code below, anytime a planet increases or decreases in population (just check all instances of pl_armies), call planet_delta() with the pointer to the planet struct. Oh, and if the planet is made independent, remove the PLPOP and PLPLAGUE flags. I have context diffs off the rwd4 code (probably bronco based at some point) if you really want to try them. set PLANETFUSE to 1, doing otherwise might not be too bad but you'll be skewing the distribution by artificially lengthening the interarrival times. place this code where the old udplanets was.... float poisson_interval(rate) float rate; { double tmp; while ((tmp=((random()&0xffffff)/(double) 0x1000000)) == 0); return (float) (-log(tmp)/rate); } planet_delta(l) register struct planet *l; { float rate, pop_delta, plague_delta; l->pl_flags &= ~(PLPOP|PLPLAGUE); if (l->pl_armies<4) if (l->pl_flags&PLAGRI) rate = 29.0/1600; /* magic bronco numbers -- these are in */ else /* armies/2/sec since the average pop */ rate = 5.0/1600; /* size is currently 2 */ else if (l->pl_flags&PLAGRI) rate = 1.0/200; else rate = 1.0/400; pop_delta = poisson_interval(rate); /* now calculate time to next plague */ rate = l->pl_armies/75.0; /* magic bronco again */ plague_delta = poisson_interval(rate); if (plague_deltapl_deadtime = ticks+plague_delta*1000000/UPDATE; l->pl_flags |= PLPLAGUE; } else { l->pl_deadtime = ticks+pop_delta*1000000/UPDATE; l->pl_flags |= PLPOP; } if (!planet_id || l->pl_deadtimepl_deadtime) planet_id = l; } planet_pop() { int cnt = planet_id->pl_armies; planet_id->pl_armies += (random()%3)+1; if (cnt<5 && planet_id->pl_armies>4) planet_id->pl_flags |= PLREDRAW; planet_delta(planet_id); } planet_plague() { int cnt = planet_id->pl_armies; planet_id->pl_armies -= random()%planet_id->pl_armies; if (cnt>4 && planet_id->pl_armies<5) planet_id->pl_flags |= PLREDRAW; planet_delta(planet_id); } udplanets() { register int i; register struct planet *l; static int first = 1; if (first) { /* this should probably be moved to some */ first = 0; /* area strictly for initialization */ planet_id = NULL; for (i=0, l=&planets[0]; ipl_owner != NOBODY) planet_delta(l); } /* no valid pop or plague times */ if (!planet_id) return; while (planet_id->pl_deadtime<=ticks) { /* only true if something */ if (planet_id->pl_flags&PLPOP) /* has to happen */ planet_pop(); else if (planet_id->pl_flags&PLPLAGUE) planet_plague(); planet_id = NULL; for (i=0, l=&planets[0]; ipl_flags & (PLPOP|PLPLAGUE)) { planet_id = l; break; } for (; ipl_flags & (PLPOP|PLPLAGUE)) && (l->pl_deadtimepl_deadtime)) planet_id = l; } } ------------------------------ From: fadden@uts.amdahl.com (Andy McFadden) Newsgroups: rec.games.netrek Subject: Server quirk diagnosis program Date: 2 Feb 93 22:17:54 GMT The problem with miramar.cad.gatech.edu (and a server in Italy, until they figured it out and fixed it many months ago) is that it's sending junk to the first client who connects. Sometimes daemonII or ntserv will print diagnostics out; if you are connecting through inetd, both stdout and stderr get sent over the line. For example, instead of seeing something like: ^K^@^@^@^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T ^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T^T ^T^T^T^@^K^@^@^@^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P^P upon connection, a client connecting to miramar would see: Starting daemon... Daemon says 'hello!' No global file. Resetting all stats inetd_dummy: is not an identifier ^K^AnlConnection to server established at 5:08pm.^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^ @^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^K^Anl ^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@ This only happens to the FIRST client connecting, because the messages are only appearing when the daemon is starting up. Since MS-II acts like a client, if the server is empty it will be unable to retrieve the server status. (Of course, in this particular case the output showed that the server had some OTHER problems, too.) Here's a program I threw together to look for problems like this. To use it, kill the daemon and shared memory segment, and run it with host/port as arguments. Send the output to a file. It will sit there for a while; after a few seconds, hit Ctrl-C to interrupt it (it doesn't automatically disconnect; sorry). You can look at the output with "cat -v file | more" or just "less file" if you have less. /* * Diagnose funky server problem * * Andy McFadden (fadden@uts.amdahl.com) * 2-Feb-93 */ #include #include #include #include #include #include #include #include static char buf[4096+1]; #define SOCKVERSION 4 #define CP_SOCKET 27 /* new socket for reconnection */ struct socket_cpacket { char type; /* CP_SOCKET */ char version; char pad2; char pad3; unsigned socket; }; static int peek(host, port) char *host; int port; { struct sockaddr_in addr; struct hostent *hp; int cc, sock, len; struct socket_cpacket sockPack; /* get numeric form */ if ((addr.sin_addr.s_addr = inet_addr(host)) == -1) { if ((hp = gethostbyname(host)) == NULL) { fprintf(stderr, "unknown host '%s'\n", host); return (-1); } else { addr.sin_addr.s_addr = *(long *) hp->h_addr; } } addr.sin_family = AF_INET; addr.sin_port = htons(port); if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return (-1); } if (connect(sock, &addr, sizeof(addr)) < 0) { perror("connect"); close(sock); return(-1); } fprintf(stderr, "--- connected\n"); sockPack.type=CP_SOCKET; sockPack.socket=htonl(5678); sockPack.version=(char) SOCKVERSION; fprintf(stderr, "--- sending version packet\n"); for (cc = 0, len = sizeof(sockPack) ; len -= cc ; len && (cc > 0)) cc = write(sock, &sockPack, len); if (cc < 0) { perror("write"); return (-1); } fprintf(stderr, "--- reading\n"); while (1) { int i; if ((cc = read(sock, buf, 4096)) <= 0) { if (cc < 0) perror("read"); close(sock); return(cc); } fwrite(buf, cc, 1, stdout); fflush(stdout); } } main(argc, argv) int argc; char *argv[]; { int port; if (argc != 3) { fprintf(stderr, "usage: %s host port\n", argv[0]); exit(2); } port = atoi(argv[2]); peek(argv[1], port); exit(0); /*NOTREACHED*/ } ------------------------------ Newsgroups: rec.games.netrek From: lyodav@iastate.edu (Sun God) Subject: Server bug fix Date: Fri, 4 Dec 1992 01:51:20 GMT Here's a minor bug fix to the bronco.final source. Here's the comment by the code: /* Above incantation is a magic formula ... (hack). */ So I guess that everyone knows that it's "magic". Here was the old code in daemonII.c: j->p_desspeed = j->p_ship.s_recharge/j->p_ship.s_warpcost + 2; This bit of code is the reason that scouts slow to warp 6 when they run out of fuel, instead of say warp 8. It works ok with all of the existing ships. However, I was messing around with getship.c and strange things were happening when ships ran out of fuel. (they would get locked at a warp at which they lost fuel) The fix is pretty simple. Here's the diff: 514c514 < j->p_desspeed = (j->p_ship.s_recharge * 2 / j->p_ship.s_warpcost) - 1; --- > j->p_desspeed = j->p_ship.s_recharge/ j->p_ship.s_warpcost + 2; This ensures that the ship will go to some warp at which it actually gains fuel. :-) --Dave/WeenieBlaster ------------------------------ Newsgroups: rec.games.netrek From: rmuliana@IASTATE.EDU (Rony Muliana) Subject: bronco final code and a small bug fix Date: Mon, 15 Feb 1993 14:46:28 GMT I am sure this is an old bug but it's not in the final bronco source. That is, when an SB is trying to declare war while there are players docked on him, he will get ghosbusted if the lines marked with + below are missing: in interface.c, declare_war(): } else if (me->p_ship.s_type == STARBASE) { if (me->p_docked > 0) { for(i=0; ip_port[i] == VACANT) /* isae -- Ted's fix */ + continue; if (mask & players[me->p_port[i]].p_team) { players[me->p_port[i]].p_flags &= ~PFDOCK; me->p_docked--; me->p_port[i] = VACANT; } } } } Btw, Doorstop server code on tbird ftp site already has it. -Rony ------------------------------ From: hadley@cad.ics.uci.edu (Tedd Hadley) Subject: Re: Server Bug Newsgroups: rec.games.netrek Date: 25 Jan 93 18:02:46 GMT In gc2n+@andrew.cmu.edu (Greg K Chung) writes: >We recently found on bronco.ece (and therefore many other servers) a >disturbing bug: >A SB, doing nothing, at 60K fuel. >A BB, doing nothing, docks on the SB. Nothing extraordinary happens. >BUT: When the BB turns its shields on, even at full fuel, and still >doing nothing (SB shields down, doing nothing) the SB LOSES FUEL. >Given that the BB generates enough fuel for its own shields at warp 0, >and the SB more then generates that much, how can the net effect be a >LOSS of fuel? Gosh, I was just going to post something about this. When I made SBs able to dock on SBs at vlsi the problem became quite pronounced. (post follows) There's a slight bookkeeping problem with sb docker refueling. Anyone who's played a base before has probably noticed the alarming rate your fuel goes down if a player is docked on you with their shields up. The following lines in udplayers()'daemonII.c (a few lines below the /* Add fuel */ comment) are the cause: } else if ((j->p_flags & PFDOCK) && (j->p_fuel < j->p_ship.s_maxfuel)) { if (players[j->p_docked].p_fuel > SBFUELMIN) { j->p_fuel += 12*j->p_ship.s_recharge; players[j->p_docked].p_fuel -= 12*j->p_ship.s_recharge; } Since a ship is charged 2-3 fuel points for having shields up, its fuel level is always below maximum fuel when the daemon gets to this statement. So the starbase is charged 12*12 fuel points (for a CA, for example) each 1/10th of a second. I could see some kind of rational behind this if a starbase's shields were strengthened by some proportionate factor for every ship docked with shields up .. but that's not the way it works. Unless anyone has any strong objections, a simple fix is: } else if ((j->p_flags & PFDOCK) && (j->p_fuel < j->p_ship.s_maxfuel)) { if (players[j->p_docked].p_fuel > SBFUELMIN) { + int fc = MIN(12*j->p_ship.s_recharge, + j->p_ship.s_maxfuel - j->p_fuel); + j->p_fuel += fc; + players[j->p_docked].p_fuel -= fc; } [MIN, if you don't have it, is #define MIN(a,b) ((a) < (b) ? (a) : (b))] ++ Tedd Hadley (hadley@uci.edu) ------------------------------ From: leonard@mimsy.umd.edu (Leonard Dickens) Newsgroups: rec.games.netrek Subject: Re: Server Bug Date: 25 Jan 93 23:15:30 GMT Tedd Hadley writes: > There's a slight bookkeeping problem with sb docker refueling. > Anyone who's played a base before has probably noticed the alarming > rate your fuel goes down if a player is docked on you with their > shields up... > [suggested fix] Hadley's fix looks OK to me, but it does have the problem that a ship still cannot generate its own fuel while docked, but MUST take fuel from the base... which is still against what one would expect, given that at ALL other times in the game the ship DOES generate its own fuel. Here is a fix of hadley's fix, that allows every ship to generate its own fuel before sucking from any nearby base. This fixes the slight weirdness you can see at vsli, where a BB docked on an SB reduces its fuel by 3 (shield cost) if the SB is lower numbered on the player list than the BB is. (The SB get its fuel limited to maxfuel before the BB draws its needed three, leaving the SB at the end of the update with 59997 fuel.) Put it in daemon.c line 668 or so, replacing the old if-then-else-else structure. /* Add fuel */ j->p_fuel += 2 * j->p_ship.s_recharge; if ((j->p_flags & PFORBIT) && (planets[j->p_planet].pl_flags & PLFUEL) && (!(planets[j->p_planet].pl_owner & (j->p_swar | j->p_hostile)))) { j->p_fuel += 6 * j->p_ship.s_recharge; } else if ((j->p_flags & PFDOCK) && (j->p_fuel < j->p_ship.s_maxfuel) && (players[j->p_docked].p_fuel > SBFUELMIN)) { int fc = MIN(10*j->p_ship.s_recharge, j->p_ship.s_maxfuel - j->p_fuel); j->p_fuel += fc; players[j->p_docked].p_fuel -= fc; } } -Leonard ------------------------------ Newsgroups: rec.games.netrek From: gosselin@ll.mit.edu (Dave Gosselin) Subject: Server Patch Date: Fri, 6 Nov 92 22:20:28 GMT I figured this patch may be of intrest to some. It is a patch to the final bronco server code to allow the server god to move the server files around without recompiling. This is done by allowing the server god to set an environment variable (the default is NTSERV_PATH). The code checks to see if theis variable is set and if so looks for the appropriate server files (.global, .planets, robotII, et al) in that directory. This mod also allows a binary server to be distributed if people wish to. Well enough useful info, back to the flames. Does this make me a server hack 8-) dave aka: Tom Servo, Crow T. Robot, BermudaTriangle, soma gosselin@ll.mit.edu *** Makefile Fri Nov 6 16:08:40 1992 --- ../../ntserv/Makefile Fri Nov 6 13:58:18 1992 *************** *** 31,43 **** timecheck.o \ torp.o \ util.o \ ! warning.o D_OBJS = daemonII.o \ sintab.o \ sysdefaults.o \ wander2.o \ ! data.o # random.o -- put this back if you have a better rand() r_OBJS = data.o \ --- 31,45 ---- timecheck.o \ torp.o \ util.o \ ! warning.o\ ! getpath.o D_OBJS = daemonII.o \ sintab.o \ sysdefaults.o \ wander2.o \ ! data.o \ ! getpath.o # random.o -- put this back if you have a better rand() r_OBJS = data.o \ *************** *** 53,59 **** startdaemon.o \ sysdefaults.o \ torp.o \ ! util.o P_OBJS = data.o \ detonate.o \ --- 55,62 ---- startdaemon.o \ sysdefaults.o \ torp.o \ ! util.o \ ! getpath.o P_OBJS = data.o \ detonate.o \ *************** *** 68,74 **** startdaemon.o \ sysdefaults.o \ torp.o \ ! util.o R_FILES = coup.c \ data.c \ --- 71,78 ---- startdaemon.o \ sysdefaults.o \ torp.o \ ! util.o \ ! getpath.o R_FILES = coup.c \ data.c \ *************** *** 94,100 **** timecheck.c \ torp.c \ util.c \ ! warning.c SRC = $(R_FILES) \ data.h \ --- 98,105 ---- timecheck.c \ torp.c \ util.c \ ! warning.c \ ! getpath.c SRC = $(R_FILES) \ data.h \ *************** *** 104,115 **** packets.h \ copyright.h ! LIBS = -lm ! CFLAGS = -O -DFULL_HOSTNAMES -I/usr/X11R5/include #CFLAGS = -g ! all: ntserv daemonII robotII ntserv: $(R_OBJS) tags cc $(CFLAGS) -o ntserv $(R_OBJS) $(LIBS) --- 109,120 ---- packets.h \ copyright.h ! LIBS = -lm ! CFLAGS = -O -DFULL_HOSTNAMES -I/usr/X11R5/include #CFLAGS = -g ! all: ntserv daemonII robotII puck ntserv: $(R_OBJS) tags cc $(CFLAGS) -o ntserv $(R_OBJS) $(LIBS) *************** *** 127,136 **** cc $(CFLAGS) -o puck $(P_OBJS) $(LIBS) chmod 4751 puck ! install: ntserv ! -mv /usr/users/terence/bin/ntserv /usr/users/terence/bin/ntserv.old ! cp ntserv /usr/users/terence/bin/ntserv ! chmod 4751 /usr/users/terence/bin/ntserv -mv $(XTREKDIR)/daemonII $(XTREKDIR)/daemonII.old cp daemonII $(XTREKDIR) chmod 4751 $(XTREKDIR)/daemonII --- 132,141 ---- cc $(CFLAGS) -o puck $(P_OBJS) $(LIBS) chmod 4751 puck ! install: ntserv daemonII robotII puck ! -mv $(DESTDIR)/ntserv $(DESTDIR)/ntserv.old ! cp ntserv $(DESTDIR)/ntserv ! chmod 4751 $(DESTDIR)/ntserv -mv $(XTREKDIR)/daemonII $(XTREKDIR)/daemonII.old cp daemonII $(XTREKDIR) chmod 4751 $(XTREKDIR)/daemonII *************** *** 139,144 **** --- 144,157 ---- chmod 4751 $(XTREKDIR)/robotII touch $(XTREKDIR)/.planets $(XTREKDIR)/.motd chmod 600 $(XTREKDIR)/.planets + -mv $(DESTDIR)/puck $(DESTDIR)/puck.old + cp puck $(DESTDIR) + chmod 4751 $(DESTDIR)/puck + + clean: + rm -f $(R_OBJS) $(D_OBJS) $(r_OBJS) $(P_OBJS) ntserv daemonII + rm -f robotII puck *~ #*# + tags: ctags $(SRC) *** defs.h Sat Aug 1 23:38:03 1992 --- ../../ntserv/defs.h Fri Nov 6 13:37:34 1992 *************** *** 96,112 **** #define TARG_SELF 0x8 /* Data files to make the game play across daemon restarts. */ ! #define GLOBAL "/usr/users/terence/lib/xtrekII/.global" ! #define SCORES "/usr/users/terence/lib/xtrekII/.scores" ! #define PLFILE "/usr/users/terence/lib/xtrekII/.planets" ! #define MOTD "/usr/users/terence/lib/xtrekII/.motd" ! #define DAEMON "/usr/users/terence/lib/xtrekII/daemonII" ! #define ROBOT "/usr/users/terence/lib/xtrekII/robotII" ! #define LOGFILENAME "/usr/users/terence/lib/xtrekII/logfile" ! #define PLAYERFILE "/usr/users/terence/lib/xtrekII/.players" ! #define CONQFILE "/usr/users/terence/lib/xtrekII/.conquer" ! #define SYSDEF_FILE "/usr/users/terence/lib/xtrekII/.sysdef" /* Other stuff that Ed added */ --- 96,132 ---- #define TARG_SELF 0x8 /* Data files to make the game play across daemon restarts. */ + /* change 11/6/92 DRG make these into real variables + #define GLOBAL "/home/gosselin/lib/xtrekII/.global" + #define SCORES "/home/gosselin/lib/xtrekII/.scores" + #define PLFILE "/home/gosselin/lib/xtrekII/.planets" + #define MOTD "/home/gosselin/lib/xtrekII/.motd" + #define DAEMON "/home/gosselin/lib/xtrekII/daemonII" + #define ROBOT "/home/gosselin/lib/xtrekII/robotII" + #define LOGFILENAME "/home/gosselin/lib/xtrekII/logfile" + #define PLAYERFILE "/home/gosselin/lib/xtrekII/.players" + #define CONQFILE "/home/gosselin/lib/xtrekII/.conquer" + #define SYSDEF_FILE "/home/gosselin/lib/xtrekII/.sysdef" + */ ! /* Environment variable name. added 11/6/92 DRG */ ! #define ENV_NAME "NTSERV_PATH" ! ! /* Default path if the environment variable is not found. added 11/6/92 DRG */ ! #define DEFAULT_PATH "/home/gosselin/lib/xtrekII" ! ! /* Names of the appropriate files. added 11/6/92 DRG */ ! ! #define N_GLOBAL ".global" ! #define N_SCORES ".scores" ! #define N_PLFILE ".planets" ! #define N_MOTD ".motd" ! #define N_DAEMON "daemonII" ! #define N_ROBOT "robotII" ! #define N_LOGFILENAME "logfile" ! #define N_PLAYERFILE ".players" ! #define N_CONQFILE ".conquer" ! #define N_SYSDEF_FILE ".sysdef" /* Other stuff that Ed added */ *** data.h Sun Aug 2 02:09:07 1992 --- ../../ntserv/data.h Fri Nov 6 13:37:37 1992 *************** *** 134,136 **** --- 134,151 ---- extern char login[PSEUDOSIZE]; extern struct rank ranks[NUMRANKS]; + + /* make the files as real character values. added 11/6/92 DRG */ + + #define FNAMESIZE 256 + + extern char GLOBAL[FNAMESIZE]; + extern char SCORES[FNAMESIZE]; + extern char PLFILE[FNAMESIZE]; + extern char MOTD[FNAMESIZE]; + extern char DAEMON[FNAMESIZE]; + extern char ROBOT[FNAMESIZE]; + extern char LOGFILENAME[FNAMESIZE]; + extern char PLAYERFILE[FNAMESIZE]; + extern char CONQFILE[FNAMESIZE]; + extern char SYSDEF_FILE[FNAMESIZE]; *** data.c Sun Aug 2 02:09:04 1992 --- ../../ntserv/data.c Fri Nov 6 13:37:36 1992 *************** *** 55,61 **** int showgalactic = 2; char *shipnos="0123456789abcdefghijklmnopqrstuvwxyz"; int sock= -1; ! int xtrekPort=27320; int shipPick; int teamPick; int repCount=0; --- 55,61 ---- int showgalactic = 2; char *shipnos="0123456789abcdefghijklmnopqrstuvwxyz"; int sock= -1; ! int xtrekPort=2592; int shipPick; int teamPick; int repCount=0; *************** *** 117,119 **** --- 117,132 ---- {30.0, 7.0, 0.0, "Rear Adm."}, {40.0, 8.0, 0.0, "Admiral"}}; + + /* make the files as real character values. added 11/6/92 DRG */ + + char GLOBAL[FNAMESIZE]; + char SCORES[FNAMESIZE]; + char PLFILE[FNAMESIZE]; + char MOTD[FNAMESIZE]; + char DAEMON[FNAMESIZE]; + char ROBOT[FNAMESIZE]; + char LOGFILENAME[FNAMESIZE]; + char PLAYERFILE[FNAMESIZE]; + char CONQFILE[FNAMESIZE]; + char SYSDEF_FILE[FNAMESIZE]; *** main.c Sat Aug 1 23:38:04 1992 --- ../../ntserv/main.c Fri Nov 6 13:58:15 1992 *************** *** 47,52 **** --- 47,53 ---- name = *argv++; argc--; + getpath(); /* added 11/6/92 DRG */ if ((ptr = rindex(name, '/')) != NULL) name = ptr + 1; while (*argv) { *** puck.c Sat Aug 1 23:38:05 1992 --- ../../ntserv/puck.c Fri Nov 6 13:58:16 1992 *************** *** 58,63 **** --- 58,64 ---- char str[80]; int class; /* ship class 8/9/91 TC */ + getpath(); /* added 11/6/92 DRG */ class = STARBASE; target = -1; /* no target 7/27/91 TC */ for( ; argc>1 && argv[1][0]=='-'; argc--,argv++) { *** daemonII.c Sat Aug 1 23:38:03 1992 --- ../../ntserv/daemonII.c Fri Nov 6 13:58:13 1992 *************** *** 64,69 **** --- 64,70 ---- debug = 1; fprintf(stderr,"Daemon says 'hello!'\n"); + getpath(); /* added 11/6/92 DRG */ srandom(getpid()); readsysdefaults(); if (!debug) { *** robotII.c Sat Aug 1 23:38:06 1992 --- ../../ntserv/robotII.c Fri Nov 6 13:58:12 1992 *************** *** 58,63 **** --- 58,64 ---- char str[80]; int class; /* ship class 8/9/91 TC */ + getpath(); /* added 11/6/92 DRG */ class = BATTLESHIP; target = -1; /* no target 7/27/91 TC */ for( ; argc>1 && argv[1][0]=='-'; argc--,argv++) { *** /dev/null Fri Nov 6 15:01:05 1992 --- ../../ntserv/getpath.c Fri Nov 6 14:32:34 1992 *************** *** 0 **** --- 1,62 ---- + /* + * getpath.c + * + * David Gosselin 11/6/92 + * + */ + + #include + #include + #include + #include + #include "defs.h" + #include "data.h" + + getpath() + { + char *env_path; + char path[256]; + + if ((env_path = getenv(ENV_NAME)) != NULL) { + strcpy(path,env_path); + fprintf(stderr,"Got path %s\n",env_path); + } + else { + strcpy(path,DEFAULT_PATH); + fprintf(stderr,"Unable to find environment variable %s.",ENV_NAME); + fprintf(stderr," Assuming: %s\n",DEFAULT_PATH); + } + + strcat(path,"/"); + + strcpy(GLOBAL,path); + strcat(GLOBAL,N_GLOBAL); + + strcpy(SCORES,path); + strcat(SCORES,N_SCORES); + + strcpy(PLFILE,path); + strcat(PLFILE,N_PLFILE); + + strcpy(MOTD,path); + strcat(MOTD,N_MOTD); + + strcpy(DAEMON,path); + strcat(DAEMON,N_DAEMON); + + strcpy(ROBOT,path); + strcat(ROBOT,N_ROBOT); + + strcpy(LOGFILENAME,path); + strcat(LOGFILENAME,N_LOGFILENAME); + + strcpy(PLAYERFILE,path); + strcat(PLAYERFILE,N_PLAYERFILE); + + strcpy(CONQFILE,path); + strcat(CONQFILE,N_CONQFILE); + + strcpy(SYSDEF_FILE,path); + strcat(SYSDEF_FILE,N_SYSDEF_FILE); + + } ------------------------------ From: hadley@thoth.ics.uci.edu (Tedd Hadley) Subject: SERVER OPTIMIZATION (too good to be true?) Newsgroups: rec.games.netrek Date: 15 Dec 92 04:10:12 GMT (The following relates to slight change to the server (input.c) that greatly reduces the load put on the server host machine.) First, the results: Server code: INL3.0 machine: sparc 1 (4/60, 12Mb ram) players: 16 (IBL robots) at 5 updates/s Before the change, the load average checked once per minute: 0.46 2.12 5.59 5.54 5.16 5.14 6.91 7.68 7.43 7.33 7.78 5.52 5.46 4.89 5.19 5.52 7.13 7.68 6.76 6.39 5.73 5.64 4.95 5.31 6.03 6.09 ... averaging 5.8 over 30 minutes (sparc 1 is the slowest sparc) After the change: 0.37 0.53 0.44 1.46 1.81 2.00 1.49 1.28 0.57 0.43 0.32 0.14 0.22 0.30 0.46 1.65 1.53 1.22 1.44 0.81 0.38 1.17 0.82 1.14 1.20 1.81 ... averaging 0.9 over 30 minutes. Server code: INL3.0 machine: sparc 2 (4/75, 20Mb ram) players: 16 (IBL robots) at 5 updates/s Before the change: 0.75 1.36 1.88 3.47 3.48 3.64 4.34 3.90 2.79 3.43 3.03 2.31 2.36 3.59 3.92 4.80 4.83 4.36 3.20 2.91 3.32 1.93 1.91 3.43 2.04 1.62 ... averaging 2.9 over 30 minutes. After the change: 0.27 0.21 0.14 0.14 0.04 0.79 1.02 0.70 0.39 0.13 0.19 0.59 0.34 0.11 0.22 0.13 0.96 0.33 0.26 0.52 0.21 0.06 0.27 0.12 0.03 0.57 ... averaging 0.3 over 30 minutes. So what the hell's the change? Well, all I did was replace the 50,000 ms (pseudo-polling) loop in input.c with one that cycles at timerDelay (200,000 ms at 5 update/s), but stills catches user input as it's available. The result seems to be at least a factor of 4 improvement in running efficiency, and with no change apparent to the client (other then lack of server lag). Since I'm taking off for Christmas vacation in a couple days I thought I'd post the fix now for anyone who's interested. The only change is to input.c and the file I'm appending should plug in directly to the INL, bronco, or UDP sources (no variables required). I've been running the fix on vlsi.ics.uci.edu for the last few days .. everything seems to be fine. Give it a try if you want to drop your server machine's load average by a few points (note: I don't know yet if DEC workstations will show the same improvement). Disclaimer: as with any new change, I can't guarantee that there isn't some other bug hidden somewhere else, so keep a backup handy :) #include "copyright.h" #include #include #include #include #include #include #include "defs.h" #include "struct.h" #include "data.h" static int sendflag=0; static int do_update=1; setflag() { if(do_update) intrupt(); else sendflag=1; /* statements below are executed once per timerDelay */ /* note that these two statements were originally expected to be checked once per 50000 (rather then once per timerDelay), so we duplicate the check after each readFromClient, below */ if (me->p_updates > delay) { me->p_flags &= ~(PFWAR); } if (me->p_updates > rdelay) { me->p_flags &= ~(PFREFITTING); } /* Keep self from being nuked... */ me->p_ghostbuster = 0; } input() { int fd, nchar; struct itimerval udt; struct timeval timeout; fd_set readfds; int s; udt.it_interval.tv_sec = 0; udt.it_interval.tv_usec = timerDelay; udt.it_value.tv_sec = 0; udt.it_value.tv_usec = timerDelay; setitimer(ITIMER_REAL, &udt, 0); signal(SIGALRM, setflag); /* Idea: read from client often, send to client not so often */ while (1) { if (isClientDead()) { if(!reconnect()) exit(0); } FD_ZERO(&readfds); FD_SET(sock, &readfds); if (udpSock >= 0) FD_SET(udpSock, &readfds); /* blocks indefinitely unless client input or timerDelay SIGALRM causes EINTR */ if(select(32, &readfds, 0, 0, NULL) > 0){ do_update = 0; /* don't let updates interfere with read */ if (me->p_updates > delay) { me->p_flags &= ~(PFWAR); } if (me->p_updates > rdelay) { me->p_flags &= ~(PFREFITTING); } readFromClient(); do_update = 1; /* ok now */ /* I think this can happen if a client read was interrupted */ if (sendflag) { intrupt(); sendflag=0; } } } } reconnect() { int i; char buf[80]; struct itimerval udt; if (noressurect) exitGame(); udt.it_interval.tv_sec = 0; udt.it_interval.tv_usec = 0; udt.it_value.tv_sec = 0; udt.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &udt, 0); signal(SIGALRM, SIG_IGN); signal(SIGIO, SIG_IGN); printf("Ack! The client went away!\n"); printf("I will attempt to resurrect him!\n"); fflush(stdout); commMode = COMM_TCP; if(udpSock >= 0) closeUdpConn(); /* For next two minutes, we try to restore connection */ shutdown(sock, 2); sock= -1; for (i=0;; i++) { me->p_ghostbuster = 0; sleep(5); if (connectToClient(host, nextSocket)) break; if (i==23) { printf("Oh well, maybe I'm getting rusty!\n"); fflush(stdout); return 0; } } udt.it_interval.tv_sec = 0; udt.it_interval.tv_usec = timerDelay; udt.it_value.tv_sec = 0; udt.it_value.tv_usec = timerDelay; setitimer(ITIMER_REAL, &udt, 0); signal(SIGALRM, setflag); printf("A miracle! He's alive!\n"); fflush(stdout); return 1; } ------------------------------ From: lab@mtek.chalmers.se (Lars Bernhardsson) Newsgroups: rec.games.netrek Subject: SERVER MOD: What RSA clients are used? Date: 1 Apr 93 03:15:14 GMT Tjaba! I got inspired about the talk about server mods, so I'll post one that I made. With this, if you send a '#' to a player, the server will reply with a message on the form: GOD->F0 Client: Berkeley Client / Sun4 / SunOS 4.1.1 Always nice to know what clients people are using, eh? These changes are needed: (btw, I started with the Doorstop source) --- data.c #ifdef RSA int RSA_Client char RSA_client_type[256]; /* LAB 4/1/93 */ #endif --- data.h: #ifdef RSA extern int RSA_Client; extern char RSA_client_type[256]; /* LAB 4/1/93 */ #endif --- rsa_key.c: In the end of this file: fclose(logfile); sprintf(RSA_client_type, "%s / %s", /* LAB 4/1/93 */ key.client_type, key.architecture); RSA_client_type[64] = '\0'; return 0; } --- socket.c: Near line 966: /* session stats now parsed here. parseQuery == true */ /* means eat message 4/17/92 TC */ if (!parseQuery(&msg)) if(!parseRSAquery(&msg)) /* LAB 4/1/93 */ if (ignored[cur->m_from] & MINDIV) bounce("That player is currently ignoring you.", cur->m_from); else sendClientPacket(&msg); } --- socket.c: Anywhere you like add this function: int parseRSAquery(struct mesg_spacket *packet) { char buf[82]; if(packet->mesg[10] != '#') return FALSE; sprintf(buf, "Client: %s", RSA_client_type); bounce(buf, packet->m_from); return TRUE; } --- That's it. Hopefully I didn't forget anything... I know the code probably looks ugly, but I've never been to a school to learn how to do "real" programming, so no flames please. (constructive criticism is of course very welcome) Lars Bernhardsson (lab@mtek.chalmers.se) Servergod @ iota42.mtek.chalmers.se, on the sunny side of Sweden. ------------------------------ Newsgroups: rec.games.netrek From: hde+@cs.cmu.edu (Herbert Enderton) Subject: Why Ceti is always agri Date: Fri, 2 Apr 1993 01:37:13 GMT As people have observed over the past year, Ceti is an agricultural world disproportionately often in INL games. There is in fact a reason for this, and it is a simple bug: Ceti is identified as a core planet for the Feds in the definition of core_planets. The simple fix is change the line in daemonII.c that says {{ 7, 6, 5, 8,}, to {{ 7, 9, 5, 8,}, Planet 6 is Ceti, planet 9 is Alpha. Ever notice that Alpha is never agri in the INL server? (Nor are Org, Reg, Lal, Spi, or the homeworlds.) The resource assigning procedure puts in one core agri (using a different definition of core world than is used elsewhere in the server) and one perimeter agri. Ceti is both, so if I'm reading the code correctly it will be agri 44% of the time, and there's some chance that it will be the Fed's ONLY agri! Actually I think the whole resource-assigning algorithm looks pretty dubious. For instance, was it intentional that the perimeter agri can never have fuel? Or that each race has one front with no fuel? With a careful rewrite, doResources would generate more variety without losing fairness. I'm willing try rewriting it if ERic and the council so desire. Or we could do away with that code altogether, use the standard bronco-release resource code, and allow the home-team to reset resources a few times before the visiting team shows up (I hear MUCUS does this anyway), effectively employing the cut-and-choose method of ensuring fairness. -- Red Shirt ------------------------------ From: lab@mtek.chalmers.se (Lars Bernhardsson) Newsgroups: rec.games.netrek Subject: SERVER MOD: What RSA clients are used? Date: 1 Apr 93 03:15:14 GMT Tjaba! I got inspired about the talk about server mods, so I'll post one that I made. With this, if you send a '#' to a player, the server will reply with a message on the form: GOD->F0 Client: Berkeley Client / Sun4 / SunOS 4.1.1 Always nice to know what clients people are using, eh? These changes are needed: (btw, I started with the Doorstop source) --- data.c #ifdef RSA int RSA_Client char RSA_client_type[256]; /* LAB 4/1/93 */ #endif --- data.h: #ifdef RSA extern int RSA_Client; extern char RSA_client_type[256]; /* LAB 4/1/93 */ #endif --- rsa_key.c: In the end of this file: fclose(logfile); sprintf(RSA_client_type, "%s / %s", /* LAB 4/1/93 */ key.client_type, key.architecture); RSA_client_type[64] = '\0'; return 0; } --- socket.c: Near line 966: /* session stats now parsed here. parseQuery == true */ /* means eat message 4/17/92 TC */ if (!parseQuery(&msg)) if(!parseRSAquery(&msg)) /* LAB 4/1/93 */ if (ignored[cur->m_from] & MINDIV) bounce("That player is currently ignoring you.", cur->m_from); else sendClientPacket(&msg); } --- socket.c: Anywhere you like add this function: int parseRSAquery(struct mesg_spacket *packet) { char buf[82]; if(packet->mesg[10] != '#') return FALSE; sprintf(buf, "Client: %s", RSA_client_type); bounce(buf, packet->m_from); return TRUE; } --- That's it. Hopefully I didn't forget anything... I know the code probably looks ugly, but I've never been to a school to learn how to do "real" programming, so no flames please. (constructive criticism is of course very welcome) Lars Bernhardsson (lab@mtek.chalmers.se) Servergod @ iota42.mtek.chalmers.se, on the sunny side of Sweden. ------------------------------ End of SERVER PATCHES *********************