comparison main.c @ 3:5a977ccbc7a9 default tip

Empty changelog
author darius
date Sat, 06 Dec 1997 05:41:29 +0000
parents
children
comparison
equal deleted inserted replaced
2:fba0b6e6cdc7 3:5a977ccbc7a9
1 /* $Id: main.c,v 1.1.1.1 1997/12/06 05:41:29 darius Exp $ */
2
3 /*
4 * main.c
5 */
6 #include "copyright.h"
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <sys/types.h>
11 #include <signal.h>
12 #include <setjmp.h>
13 #include <pwd.h>
14 #ifdef hpux
15 #include <time.h>
16 #else
17 #include <sys/time.h>
18 #include <sys/wait.h>
19 #endif /* hpux */
20 #include "Wlib.h"
21 #include "defs.h"
22 #include "struct.h"
23 #include "data.h"
24 #include "packets.h"
25 #include "proto.h"
26 #include "gameconf.h"
27 #ifdef SOUND
28 #include "Slib.h"
29 #endif
30 #include "sound.h"
31
32 jmp_buf env;
33
34 #ifdef AMIGA
35 /* needed for metafork... implemented by running a whole new copy
36 don't honestly know why I bothered ;-) */
37 char **command_line;
38 int global_argc;
39 #endif
40
41 #ifdef GATEWAY
42 #define DEFAULT_GATEWAY "atlgw" /* for Quar */
43
44 static char *get_gw P((void));
45 static unsigned long mkaddr P((char *m));
46 static void getUdpPort P((void));
47 #endif
48
49 extern int UdpLocalPort;
50 /* Prototypes */
51 static void printUsage P((char *prog));
52 static void show_credits ();
53
54 int
55 main(argc, argv)
56 int argc;
57 char **argv;
58 {
59 int intrupt();
60 int team, s_type;
61 char *dpyname = NULL;
62 int usage = 0;
63 int err = 0;
64 char *name, *ptr, *cp;
65 #if !defined(NeXT) && !defined(RS6K) && !defined(SVR4)
66 char *rindex();
67 #endif
68 struct passwd *pwent;
69 int passive = 0;
70 #ifdef METASERVER
71 int usemeta = 0;
72 #endif /* METASERVER */
73 /* char *defaultsFile=NULL;*/
74
75 pseudo[0] = defpasswd[0] = '\0';
76
77 #ifdef AMIGA
78 command_line = argv;
79 global_argc=argc;
80 #endif
81
82 name = *argv++;
83 argc--;
84 if ((ptr = rindex(name, '/')) != NULL)
85 name = ptr + 1;
86 #ifdef GATEWAY
87 netaddr = -1; /* special NULL address */
88 serverName = get_gw(); /* default machine is gw */
89 #endif
90 while (*argv) {
91 if (**argv != '-') {
92 serverName = *argv; /* don't abort argument processing */
93 argv++;
94 argc--;
95 } else {
96 ++*argv;
97
98 argc--;
99 ptr = *argv++;
100 while (*ptr) {
101 switch (*ptr) {
102 case 'C': /* character name */
103 (void) strncpy(pseudo, *argv, sizeof(pseudo));
104 argv++;
105 argc--;
106 break;
107
108 case 'c': /* Credits */
109 show_credits();
110 exit (0);
111 break;
112
113 case 'P': /* authorization password */
114 (void) strncpy(defpasswd, *argv, sizeof(defpasswd));
115 {
116 int i;
117 for (i = 0; (*argv)[i]; i++)
118 (*argv)[i] = 0;
119 }
120 argv++;
121 argc--;
122 break;
123
124 case 'u':
125 usage++;
126 break;
127 case 's':
128 if (*argv) {
129 xtrekPort = atoi(*argv);
130 passive = 1;
131 argv++;
132 argc--;
133 }
134 break;
135 case 'p':
136 if (*argv) {
137 xtrekPort = atoi(*argv);
138 argv++;
139 argc--;
140 }
141 break;
142 case 'd':
143 dpyname = *argv;
144 argc--;
145 argv++;
146 break;
147 #ifdef METASERVER
148 case 'm':
149 usemeta = 1;
150 break;
151 #endif /* METASERVER */
152 case 'h':
153 serverName = *argv;
154 if (!serverName)
155 err++;
156 #ifdef GATEWAY
157 gw_mach = *argv;
158 #endif
159 argc--;
160 argv++;
161 break;
162 #ifdef GATEWAY
163 case 'H':
164 netaddr = mkaddr(*argv);
165 argc--;
166 argv++;
167 break;
168 #endif
169
170 case 't':
171 title = *argv;
172 argc--;
173 argv++;
174 break;
175 case 'r':
176 defaultsFile = *argv;
177 argv++;
178 argc--;
179 break;
180 #ifdef AUTHORIZE
181 case 'o':
182 RSA_Client = -1;
183 break;
184 case 'R':
185 RSA_Client = -2;
186 break;
187 #else
188 case 'o':
189 case 'R':
190 printf("This client does not have binary authorization.\n");
191 break;
192 #endif
193 case 'e':
194 #ifdef AUTHORIZE
195 checkExpire(1);
196 #else
197 printf("This client does not RSA verify and will not expire.\n");
198 #endif
199 exit(0);
200 break;
201 case 'f': /* list ftp sites */
202 fprintf(stderr, "\n\
203 The newest version of the Paradise client can be found at:\n\
204 ftp.pnetrek.org in /pub/paradise/bin/\n\
205 or ftp.cs.umn.edu in /users/glamm/paradise/bin/\n\
206 \n\
207 Quick ftp instructions:\n\
208 This assumes you are bob@school.edu and are using a Sun workstation\n\
209 ('sparc' architecture). Modify with your mailing address and architecture.\n\
210 Type:\n\
211 ftp ftp.cs.umn.edu\n\
212 (at name prompt) anonymous\n\
213 (at password prompt) bob@school.edu\n\
214 (at ftp> prompt) cd users/glamm/paradise/bin\n\
215 (at ftp> prompt) binary\n\
216 (at ftp> prompt) dir\n\
217 (lots of files printed - pick out the latest release for your architecture.\n\
218 all the client binaries begin with 'netrek'.)\n\
219 (at ftp> prompt) get netrek.Sparc-SunOS-static\n\
220 (note: static and dynamic are functionally the same.\n\
221 (at ftp> prompt) bye\n\
222 \n\
223 That's it!\n");
224 exit(0);
225 case 'G':
226 if (*argv) {
227 ghoststart++;
228 ghost_pno = atoi(*argv);
229 printf("Emergency restart being attempted...\n");
230 argv++;
231 argc--;
232 }
233 break;
234 case '2': /* force paradise */
235 paradise = 1;
236 break;
237 #ifdef RECORDER
238 case 'F': /* File playback */
239 if (*argv) {
240 playFile = strdup(*argv);
241 playback = 1;
242 argv++;
243 argc--;
244 }
245 break;
246 #endif
247 case 'U':
248 if(*argv == NULL || (UdpLocalPort = atoi(*argv)) <= 0) {
249 fprintf(stderr, "Error: -U requires a port number\n");
250 usage++;
251 break;
252 }
253 argc--;
254 argv++;
255 break;
256 default:
257 fprintf(stderr, "%s: unknown option '%c'\n", name, *ptr);
258 err++;
259 break;
260 }
261 ptr++;
262 }
263 }
264 }
265 #ifdef GATEWAY
266 if (netaddr == -1) {
267 fprintf(stderr,
268 "netrek: no remote address set (-H). Restricted server will not work.\n");
269 }
270 #endif
271
272
273
274 inittrigtables();
275
276 initStars(); /* moved from redraw.c at KAO\'s suggestion */
277
278 if (usage || err) {
279 printUsage(name);
280 #ifdef AUTHORIZE
281 checkExpire(1);
282 #endif
283 exit(0);
284 /* exit(err); Exits from checkExpire */
285 }
286 defaultsFile = initDefaults(defaultsFile);
287
288 #ifdef AUTHORIZE
289 if (RSA_Client != -1)
290 checkExpire(0);
291 #endif
292
293 /* compatability */
294 if (argc > 0)
295 serverName = argv[0];
296
297 srandom(getpid() + time((long *) 0));
298
299 #ifdef RECORDER
300 if(playback || booleanDefault("playback",0)) {
301 defNickName = "playback";
302 usemeta=0;
303 serverName = "playback";
304 } else
305 #endif
306 {
307 if (serverName) {
308 char temp[80], *s;
309 sprintf(temp, "server.%s", serverName);
310 if ((s = getdefault(temp))) {
311 s=strdup(s);
312 printf("Using nickname \"%s\" for server %s\n", serverName, s);
313 defNickName = serverName;
314 serverName = s;
315 defFlavor = getdefault("flavor");
316 if(defFlavor)
317 defFlavor=strdup(defFlavor);
318 }
319 }
320 if (!serverName) {
321 if(serverName = getdefault("server"))
322 serverName = strdup(serverName);
323 }
324 if (!serverName && !passive) {
325 serverName = DEFAULT_SERVER;
326 #ifdef METASERVER
327 usemeta = 1; /* no server specified, show the menu */
328 #endif
329 }
330 if (passive)
331 serverName = "passive"; /* newwin gets a wrong title otherwise */
332
333 if (xtrekPort < 0)
334 xtrekPort = intDefault("port", -1);
335 if (xtrekPort < 0)
336 xtrekPort = DEFAULT_PORT;
337 #if 0
338 #ifdef AUTHORIZE
339 if (RSA_Client >= 0)
340 RSA_Client = booleanDefault("useRSA", RSA_Client);
341 else
342 RSA_Client = (RSA_Client == -2);
343 #endif
344 #endif /* 0 */
345
346 } /* playback */
347 build_default_configuration();
348
349 #ifdef METASERVER
350 metaserverAddress = stringDefault("metaserver",
351 "metaserver.ecst.csuchico.edu");
352 if (usemeta)
353 parsemeta();
354 #endif /* METASERVER */
355
356 W_Initialize(dpyname);
357 #ifdef SOUND
358 S_Initialize();
359 #endif
360
361
362 #ifdef METASERVER
363 metaFork = booleanDefault("metaFork", metaFork);
364 /* do the metawindow thang */
365 if (usemeta) {
366 metawindow();
367 metainput();
368 if (metaFork)
369 W_Initialize(dpyname);
370 newwin(dpyname, name);
371 } else
372 #endif /* METASERVER */
373
374 /* this creates the necessary x windows for the game */
375 newwin(dpyname, name);
376
377 #ifdef TIMELORD
378 start_timelord();
379 #endif
380
381 /* open memory...? */
382 openmem();
383 #ifdef RECORDER
384 if (!startPlayback())
385 #endif
386 {
387 if (!passive) {
388 callServer(xtrekPort, serverName);
389 } else {
390 connectToServer(xtrekPort);
391 }
392 }
393 #ifdef FEATURE
394 sendFeature("FEATURE_PACKETS", 'S', 1, 0, 0);
395 #endif
396
397 timeStart = time(NULL);
398 findslot();
399
400 /* sets all the settings from defaults file (.xtrekrc probably) */
401 resetDefaults();
402
403 #ifdef UNIX_SOUND
404 init_sound();
405 play_sound(SND_PARADISE);
406 #endif
407
408 mapAll();
409 /* signal(SIGINT, SIG_IGN);*/
410 signal(SIGCHLD, (void (*) ()) reaper);
411
412 /* Get login name */
413 if ((pwent = getpwuid(getuid())) != NULL)
414 (void) strncpy(login, pwent->pw_name, sizeof(login));
415 else
416 (void) strncpy(login, "Bozo", sizeof(login));
417 login[sizeof(login) - 1] = '\0';
418
419 if (pseudo[0] == '\0') {
420 if ((cp = getdefault("name")) != 0)
421 (void) strncpy(pseudo, cp, sizeof(pseudo));
422 else
423 (void) strncpy(pseudo, login, sizeof(pseudo));
424 }
425 pseudo[sizeof(pseudo) - 1] = '\0';
426
427 if (defpasswd[0] == '\0') {
428 char buf[100], buf2[100]; /* added password by character name -JR */
429 sprintf(buf,"password.%s",pseudo);
430 if (serverName) /* password by server name -TH */
431 sprintf(buf2, "password.%s", serverName);
432 if((cp = getdefault(buf)) || (cp = getdefault(buf2)) ||
433 (cp = getdefault("password")))
434 (void) strncpy(defpasswd, cp, sizeof(defpasswd));
435 }
436 defpasswd[sizeof(defpasswd) - 1] = '\0';
437
438 /*
439 sendLoginReq("Gray Lensman", "hh", "sfd", 0); loginAccept = -1; while
440 (loginAccept == -1) { socketPause(1,0); readFromServer(); }
441 */
442 getname(pseudo, defpasswd);
443 loggedIn = 1;
444 #ifdef TIMER
445 timeBank[T_SERVER] = timeStart;
446 timeBank[T_DAY] = 0;
447 #endif /* TIMER */
448
449
450 #ifdef AUTOKEY
451 /* autokey.c */
452 autoKeyDefaults();
453 #endif /* AUTOKEY */
454
455 /*
456 Set p_hostile to hostile, so if keeppeace is on, the guy starts off
457 hating everyone (like a good fighter should)
458 */
459 me->p_hostile = (1 << number_of_teams) - 1;
460
461 redrawTstats();
462
463 me->p_planets = 0;
464 me->p_genoplanets = 0;
465 me->p_armsbomb = 0;
466 me->p_genoarmsbomb = 0;
467 /* Set up a reasonable default */
468 me->p_whydead = KQUIT;
469 me->p_teami = -1;
470 s_type = defaultShip(CRUISER); /* from rlb7h 11/15/91 TC */
471
472 if (booleanDefault("netStats", 1))
473 startPing(); /* tell the server that we support pings */
474
475 #ifdef AUTOKEY
476 if (autoKey) {
477 /* XX: changes entire state of display */
478 W_AutoRepeatOff();
479 }
480 #endif
481 /*
482 hack to make galaxy class ships work. This could be more elegant, but
483 the configuration code would have to be modified quite a bit, since
484 the client doesn't know if it's on a paradise server until after it
485 connects, and it needs the configuration info before it connects.
486 */
487 init_galaxy_class();
488
489 initkeymap(-1); /* needs to have ship types initialized -JR */
490
491 setjmp(env); /* Reentry point of game */
492
493 if (ghoststart) {
494 int i;
495
496 ghoststart = 0;
497
498 for (i = -1; i < 5; i++)
499 if (teaminfo[i].letter == me->p_mapchars[0])
500 break;
501
502 me->p_teami = i;
503
504 if (me->p_damage > me->p_ship->s_maxdamage) {
505 me->p_status = POUTFIT;
506 } else
507 me->p_status = PALIVE;
508 } else
509 me->p_status = POUTFIT;
510
511 while (1) {
512 switch (me->p_status) {
513 case POUTFIT:
514 case PTQUEUE:
515 /* give the player the motd and find out which team he wants */
516 #if 1
517 new_entrywindow(&team, &s_type);
518 #else
519 entrywindow(&team, &s_type);
520 #endif
521 allowPlayerlist = 1;
522 if (W_IsMapped(playerw))
523 playerlist();
524
525 #ifdef RECORDER
526 if (!playback)
527 #endif
528 if (team == -1) {
529 W_DestroyWindow(w);
530 #ifdef AUTOKEY
531 if (autoKey)
532 W_AutoRepeatOn();
533 #endif
534 sendByeReq();
535 sleep(1);
536 printf("OK, bye!\n");
537 EXIT(0);
538 }
539 sendVersion();
540 myship = getship(myship->s_type);
541
542 currentship = myship->s_type;
543
544 /*
545 sendOptionsPacket(); /* this would totally blast any flags you
546 had on the server
547 */
548
549 redrawall = 1;
550 enter();
551 calibrate_stats();
552 W_ClearWindow(w);
553 /*
554 for (i = 0; i < NSIG; i++) { signal(i, SIG_IGN); }
555 */
556
557 me->p_status = PALIVE; /* Put player in game */
558
559 #ifdef UNIX_SOUND
560 kill_sound ();
561 #endif
562
563 #ifdef HOCKEY
564 hockeyInit();
565 #endif /*HOCKEY*/
566
567 #ifdef TIMER
568 timeBank[T_SHIP] = time(NULL);
569 #endif /* TIMER */
570
571 if (showStats) /* Default showstats are on. */
572 W_MapWindow(statwin);
573
574 #ifdef GATEWAY
575 /* pick a nice set of UDP ports */
576 getUdpPort();
577 #endif
578
579 #if ATM
580 if (tryUdp && commMode != COMM_UDP) {
581 sendUdpReq(COMM_UDP);
582 }
583 #endif /* ATM */
584 #ifdef SHORT_PACKETS
585 if (tryShort) {
586 sendShortReq(SPK_VON);
587 tryShort = 0; /* only try it once */
588 }
589 #endif /* SHORT_PACKETS */
590 /* Send request for a full update */
591 if (askforUpdate) {
592 if(recv_short)
593 sendShortReq(SPK_SALL);
594 else
595 sendUdpReq(COMM_UPDATE);
596 }
597 sendUpdatePacket(1000000 / updateSpeed);
598
599 W_Deiconify(baseWin);
600
601 break;
602 case PALIVE:
603 case PEXPLODE:
604 case PDEAD:
605 case POBSERVE:
606
607 #ifdef TIMELORD
608 /* reading the MOTD doesn't count against your playing time */
609 update_timelord_notcount();
610 #endif
611
612 /* Get input until the player quits or dies */
613 input();
614 W_ClearWindow(mapw);
615 #ifdef TIMELORD
616 /* get any fractional minutes we missed */
617 update_timelord(1);
618 #endif
619 break;
620 default:
621 printf("client has p_status=%d. how strange\n", me->p_status);
622 me->p_status = POUTFIT;
623 }
624 }
625
626 /* NOTREACHED */
627 }
628
629 static void
630 printUsage(prog)
631 char *prog;
632 {
633 fprintf(stderr, "Usage:\n %s [ options ] [ ntserv-host ]\n\
634 Where options are\n\
635 [-h] host server host name\n\
636 [-p] port server port number\n\
637 [-r] xtrekrc defaults file to replace ~/.xtrekrc\n\
638 [-t] title window manager title\n\
639 [-d] display set Xwindows display\n\
640 [-C] name netrek pseudonym\n\
641 [-P] passwd passwd to use to attempt autologin\n\
642 [-R] use RSA authorization (default)\n\
643 [-o] use old (non-RSA) authorization\n\
644 [-s] port wait for connection from ntserv on a port (debugging)\n\
645 %s\
646 %s\
647 [-U] port specify base local UDP port number to use\n\
648 [-e] check the expire time on the client\n\
649 [-f] how to get the newest client\n\
650 [-u] print usage\n\
651 [-c] Paradise credits\n\
652 For emergency restart:\n\
653 [-2] force paradise - use if you were on a paradise server\n\
654 [-G] playernum specify player number to use\n\
655 [-s] port specify socket number to use\n\
656 Paradise Client %s\n\
657 For more information on how to play Paradise, use Netscape or Internet\n\
658 Explorer and connect to:\n\
659 http://www.pnetrek.org/ OR\n\
660 http://www.cs.umn.edu/users/glamm/paradise/\n\n", prog,
661 #ifdef METASERVER
662 " [-m] check metaserver for active servers\n",
663 #else
664 "",
665 #endif /* METASERVER */
666 #ifdef RECORDER
667 " [-F] file Replay from file instead of connecting\n",
668 #else
669 "",
670 #endif
671 CLIENTVERS);
672 }
673
674 void
675 reaper()
676 {
677 #if defined(hpux) || defined(SVR4) || defined(AMIGA)
678 wait((int *) 0);
679 #else
680 /* well, hell, just use NULL, it works for everything else anyway */
681 /* while (wait3((union wait *) 0, WNOHANG, NULL) > 0);*/
682 while(wait3(NULL, WNOHANG, NULL) > 0);
683 #endif /* hpux */
684 }
685
686 #ifdef GATEWAY
687 static struct udpmap_t {
688 int uid;
689 int serv_port;
690 int port;
691 int local_port;
692 } udpmap[] = {
693 /* 5000, 5001, 5000 *//* generic */
694 {
695 1290, 5010, 5011, 5010
696 }, /* fadden */
697 {
698 757, 5020, 5021, 5020
699 }, /* user2 */
700 };
701 #define MAPSIZE (sizeof(udpmap) / sizeof(struct udpmap_t))
702
703 static void
704 getUdpPort()
705 {
706 int i;
707 uid_t uid;
708 char *gw_m, *gw_p, *gw_lp, *gw_sp, *err, *getenv();
709
710 /* should always be set prior, but in case not .. */
711 if (!gw_mach) {
712 gw_m = getenv("GW_MACH");
713 if (gw_m)
714 gw_mach = gw_m;
715 else
716 gw_mach = DEFAULT_GATEWAY;
717 }
718 uid = getuid();
719
720 for (i = 0; i < MAPSIZE; i++) {
721 if (uid == udpmap[i].uid) {
722 gw_serv_port = udpmap[i].serv_port;
723 gw_port = udpmap[i].port;
724 gw_local_port = udpmap[i].local_port;
725 return;
726 }
727 }
728
729 gw_p = getenv("GW_PORT");
730 gw_sp = getenv("GW_SPORT");
731 gw_lp = getenv("GW_LPORT");
732
733 if (gw_p) {
734 gw_port = strtol(gw_p, &err, 10);
735 if (err == gw_p) {
736 fprintf(stderr, "netrek: malformed integer for GW_PORT: %s\n",
737 gw_p);
738 /* let something else complain about port 0 */
739 }
740 } else
741 gw_port = 5001;
742 if (gw_sp) {
743 gw_serv_port = strtol(gw_sp, &err, 10);
744 if (err == gw_sp) {
745 fprintf(stderr, "netrek: malformed integer for GW_SPORT: %s\n",
746 gw_sp);
747 /* let something else complain about port 0 */
748 }
749 } else
750 gw_serv_port = 5000;
751
752 if (gw_lp) {
753 gw_local_port = strtol(gw_lp, &err, 10);
754 if (err == gw_lp) {
755 fprintf(stderr, "netrek: malformed integer for GW_LPORT: %s\n",
756 gw_lp);
757 /* let something else complain about port 0 */
758 }
759 } else
760 gw_local_port = 5000;
761
762 /*
763 printf("gw_mach: \'%s\'\n", gw_mach); printf("gw_local_port: %d\n",
764 gw_local_port); printf("gw_serv_port: %d\n", gw_serv_port);
765 printf("gw_port: %d\n", gw_port);
766 */
767 }
768
769 #include <sys/socket.h>
770 #include <netinet/in.h>
771 #include <netdb.h>
772
773 /*
774 * In the event of problems assiocated with the above include files the
775 * following routine can be alternately used to convert a string
776 * ("xxx.xxx.xxx.xxx") to an internet address number.
777 *
778 * (author: Andy McFadden)
779 */
780
781 #ifdef notneeded
782 unsigned long
783 dotAddrToNetAddr(str)
784 char *str;
785 {
786 char *t;
787 unsigned long answer = 0;
788 t = str;
789 for (i = 0; i < 4; i++) {
790 answer = (answer << 8) | atoi(t);
791 while (*t && *t != '.')
792 t++;
793 if (*t)
794 t++;
795 }
796 return answer;
797 }
798 #endif
799
800 /*
801 * More network "correct" routine
802 */
803
804 static unsigned long
805 mkaddr(m)
806 char *m;
807 {
808 struct in_addr ad;
809 struct hostent *hp;
810
811 hp = gethostbyname(m);
812 if (!hp) {
813 ad.s_addr = inet_addr(m);
814 if (ad.s_addr == -1) {
815 fprintf(stderr, "netrek: unknown host \'%s\'\n", m);
816 exit(1);
817 }
818 } else
819 bcopy(hp->h_addr, (char *) &ad, hp->h_length);
820
821 return ad.s_addr;
822 }
823
824 static char *
825 get_gw()
826 {
827 char *gw_m;
828
829 gw_m = getenv("GW_MACH");
830 if (gw_m)
831 gw_mach = gw_m;
832 else
833 gw_mach = DEFAULT_GATEWAY;
834
835 return gw_mach;
836 }
837
838 #endif
839
840 void
841 show_credits()
842 {
843 printf ("Paradise Netrek\n\
844 \n\
845 Copyright (c) 1986 Chris Guthrie\n\
846 Copyright (c) 1989 Kevin P. Smith\n\
847 Copyright (c) 1993 Larry Denys, Kurt Olsen, Brandon Gillespie, and\n\
848 Robert Forsman\n\
849 Copyright (c) Eric Mehlaff, Sujal Patel, Robert Glamm, \n\
850 Lars Bernhardsson, Kurt Siegl, Nick Trown\n\
851 \n\
852 Paradise Developers\n\
853 Larry Deny Robert Forsman\n\
854 Brandon Gillespie Kurt Olsen\n\
855 \n\
856 Paradise Contributors (In alphabetical order):\n\
857 Terence Chang Mike McGrath\n\
858 Bill Dyess Matthew Mead\n\
859 Jerry Frain Gary Parnes\n\
860 Robert Glamm Sujal Patel\n\
861 T. Hadley Joe Rumsey\n\
862 Heath A. Kehoe Rado Smiljanic\n\
863 Andy McFadden Joe Young\n\
864 ");
865 }