rnd-19981003-2
[rocksndiamonds.git] / src / network.c
1 /***********************************************************
2 *  Rocks'n'Diamonds -- McDuffin Strikes Back!              *
3 *----------------------------------------------------------*
4 *  (c) 1995-98 Artsoft Entertainment                       *
5 *              Holger Schemel                              *
6 *              Oststrasse 11a                              *
7 *              33604 Bielefeld                             *
8 *              phone: ++49 +521 290471                     *
9 *              email: aeglos@valinor.owl.de                *
10 *----------------------------------------------------------*
11 *  network.c                                               *
12 ***********************************************************/
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <signal.h>
18 #include <sys/types.h>
19 #include <sys/time.h>
20 #include <sys/wait.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <netinet/tcp.h>
24 #include <arpa/inet.h>
25 #include <netdb.h>
26 #include <errno.h>
27
28 #include "network.h"
29 #include "game.h"
30 #include "tape.h"
31 #include "files.h"
32 #include "misc.h"
33
34 int norestart = 0;
35 int nospeedup = 0;
36
37 #define DEFAULTPORT 19503
38
39 #define PROT_VERS_1 1
40 #define PROT_VERS_2 0
41 #define PROT_VERS_3 1
42
43 #define OP_NICK 1
44 #define OP_PLAY 2
45 #define OP_MOVE 3
46 #define OP_DRAW 4
47 #define OP_LOST 5
48 #define OP_GONE 6
49 #define OP_CLEAR 7
50 #define OP_NEW 8
51 #define OP_LINES 9
52 #define OP_GROW 10
53 #define OP_MODE 11
54 #define OP_LEVEL 12
55 #define OP_BOT 13
56 #define OP_KILL 14
57 #define OP_PAUSE 15
58 #define OP_CONT 16
59 #define OP_VERSION 17
60 #define OP_BADVERS 18
61 #define OP_MSG 19
62 #define OP_YOUARE 20
63 #define OP_LINESTO 21
64 #define OP_WON 22
65 #define OP_ZERO 23
66
67 #define MAXNICKLEN 14
68
69 struct user
70 {
71   byte nr;
72   char name[MAXNICKLEN+2];
73   struct user *next;
74 };
75
76 struct user me =
77 {
78   0,
79   "no name",
80   NULL
81 };
82
83 /* server stuff */
84
85 #define BUFLEN          4096
86
87 int sfd;
88 unsigned char realbuf[512], readbuf[BUFLEN], writbuf[BUFLEN];
89 unsigned char *buf = realbuf + 4;
90 int nread = 0, nwrite = 0;
91
92 /* like memcpy, but guaranteed to handle overlap when s <= t */
93 void copydown(char *s, char *t, int n)
94 {
95   for (; n; n--)
96     *(s++) = *(t++);
97 }
98
99 void sysmsg(char *s)
100 {
101   printf("** %s\n", s);
102   fflush(stdout);
103 }
104
105 void fatal(char *s)
106 {
107   fprintf(stderr, "%s.\n", s);
108   exit(1);
109 }
110
111 void *mmalloc(int n)
112 {
113   void *r;
114
115   r = malloc(n);
116   if (r == NULL)
117     fatal("Out of memory");
118   return r;
119 }
120
121 void u_sleep(int i)
122 {
123   struct timeval tm;
124   tm.tv_sec = i / 1000000;
125   tm.tv_usec = i % 1000000;
126   select(0, NULL, NULL, NULL, &tm);
127 }
128
129 void flushbuf()
130 {
131   if (nwrite)
132   {
133     write(sfd, writbuf, nwrite);
134     nwrite = 0;
135   }
136 }
137
138 void sendbuf(int len)
139 {
140   if (!standalone)
141   {
142     realbuf[0] = realbuf[1] = realbuf[2] = 0;
143     realbuf[3] = (unsigned char)len;
144     buf[0] = 0;
145     if (nwrite + 4 + len >= BUFLEN)
146       fatal("Internal error: send buffer overflow");
147     memcpy(writbuf + nwrite, realbuf, 4 + len);
148     nwrite += 4 + len;
149   }
150 }
151
152 struct user *finduser(unsigned char c)
153 {
154   struct user *u;
155
156   for (u = &me; u; u = u->next)
157     if (u->nr == c)
158       return u;
159   
160   fatal("Protocol error: reference to non-existing user");
161   return NULL; /* so that gcc -Wall doesn't complain */
162 }
163
164 char *get_user_name(unsigned char c)
165 {
166   struct user *u;
167
168   if (c == 0)
169     return("the server");
170   else if (c == me.nr)
171     return("you");
172   else
173     for (u = &me; u; u = u->next)
174       if (u->nr == c && u->name && strlen(u->name))
175         return(u->name);
176
177   return("no name");
178 }
179
180 void startserver()
181 {
182   char *options[2];
183   int n = 0;
184
185   options[0] = options[1] = NULL;
186   if (norestart)
187     options[n++] = "-norestart";
188   if (nospeedup)
189     options[n++] = "-nospeedup";
190
191   switch (fork())
192   {
193     case 0:
194       execlp(
195 #ifdef XTRISPATH
196       XTRISPATH "/rnd_server",
197 #else
198       "rnd_server",
199 #endif
200       "rnd_server", "-once", "-v", options[0], options[1], NULL);
201
202       fprintf(stderr, "Can't start server '%s'.\n",
203 #ifdef XTRISPATH
204         XTRISPATH "/rnd_server"
205 #else
206         "rnd_server"
207 #endif
208               );
209
210       _exit(1);
211     
212     case -1:
213       fatal("fork() failed");
214     
215     default:
216       return;
217   }
218 }
219
220 BOOL ConnectToServer(char *host, int port)
221 {
222   struct hostent *hp;
223   struct sockaddr_in s;
224   struct protoent *tcpproto;
225   int on = 1, i;
226
227   if (host)
228   {
229     if ((s.sin_addr.s_addr = inet_addr(host)) == -1)
230     {
231       hp = gethostbyname(host);
232       if (!hp)
233         fatal("Host not found");
234       s.sin_addr = *(struct in_addr *)(hp->h_addr_list[0]);
235     }
236   }
237   else
238     s.sin_addr.s_addr = inet_addr("127.0.0.1");
239
240   if (port == 0)
241     port = DEFAULTPORT;
242
243   s.sin_port = htons(port);
244   s.sin_family = AF_INET;
245   sfd = socket(PF_INET, SOCK_STREAM, 0);
246   if (sfd < 0)
247     fatal("Out of file descriptors");
248   if ((tcpproto = getprotobyname("tcp")) != NULL)
249     setsockopt(sfd, tcpproto->p_proto, TCP_NODELAY, (char *)&on, sizeof(int));
250
251   if (connect(sfd, (struct sockaddr *)&s, sizeof(s)) < 0)
252   {
253     if (!host)
254     {
255       printf("No rocksndiamonds server on localhost - starting up one ...\n");
256       startserver();
257       for (i=0; i<6; i++)
258       {
259         u_sleep(500000);
260         close(sfd);
261         sfd = socket(PF_INET, SOCK_STREAM, 0);
262         if (sfd < 0)
263           fatal("Out of file descriptors");
264         setsockopt(sfd, tcpproto->p_proto, TCP_NODELAY, (char *)&on, sizeof(int));
265         if (connect(sfd, (struct sockaddr *)&s, sizeof(s)) >= 0)
266           break;
267       }
268       if (i==6)
269         fatal("Can't connect to server");
270     }
271     else
272       fatal("Can't connect to server");
273   }
274
275   return(TRUE);
276 }
277
278 void SendToServer_Nickname(char *nickname)
279 {
280   static char msgbuf[300];
281
282   buf[1] = OP_NICK;
283   memcpy(&buf[2], nickname, strlen(nickname));
284   sendbuf(2 + strlen(nickname));
285   sprintf(msgbuf, "you set your nick to \"%s\"", nickname);
286   sysmsg(msgbuf);
287 }
288
289 void SendToServer_ProtocolVersion()
290 {
291   buf[1] = OP_VERSION;
292   buf[2] = PROT_VERS_1;
293   buf[3] = PROT_VERS_2;
294   buf[4] = PROT_VERS_3;
295   sendbuf(5);
296 }
297
298 void SendToServer_StartPlaying()
299 {
300   buf[1] = OP_PLAY;
301   buf[2] = (unsigned char)(level_nr / 256);
302   buf[3] = (unsigned char)(level_nr % 256);
303   buf[4] = (unsigned char)(leveldir_nr / 256);
304   buf[5] = (unsigned char)(leveldir_nr % 256);
305   strcpy(&buf[6], leveldir[leveldir_nr].name);
306   sendbuf(strlen(leveldir[leveldir_nr].name)+1 + 6);
307 }
308
309 void SendToServer_MovePlayer(byte player_action)
310 {
311   buf[1] = OP_MOVE;
312   buf[2] = player_action;
313   sendbuf(3);
314 }
315
316 void handlemessages()
317 {
318   unsigned int len;
319   struct user *u, *v = NULL;
320   static char msgbuf[300];
321
322   while (nread >= 4 && nread >= 4 + readbuf[3])
323   {
324     len = readbuf[3];
325     if (readbuf[0] || readbuf[1] || readbuf[2])
326       fatal("Wrong server line length");
327
328     memcpy(buf, &readbuf[4], len);
329     nread -= 4 + len;
330     copydown(readbuf, readbuf + 4 + len, nread);
331
332     switch(buf[1])
333     {
334       case OP_YOUARE:
335         printf("OP_YOUARE: %d\n", buf[0]);
336         me.nr = buf[0];
337
338         TestPlayer = buf[0] - 1;
339
340         break;
341
342       case OP_NEW:
343         printf("OP_NEW: %d\n", buf[0]);
344         sprintf(msgbuf, "new client %d connected", buf[0]);
345         sysmsg(msgbuf);
346
347         for (u = &me; u; u = u->next)
348         {
349           if (u->nr == buf[0])
350             Error(ERR_EXIT, "multiplayer server sent duplicate player id");
351           else
352             v = u;
353         }
354
355         v->next = u = mmalloc(sizeof(struct user));
356         u->nr = buf[0];
357         u->name[0] = '\0';
358         u->next = NULL;
359
360         break;
361
362       case OP_NICK:
363         printf("OP_NICK: %d\n", buf[0]);
364         u = finduser(buf[0]);
365         buf[len] = 0;
366         sprintf(msgbuf, "client %d calls itself \"%s\"", buf[0], &buf[2]);
367         sysmsg(msgbuf);
368         strncpy(u->name, &buf[2], MAXNICKLEN);
369         break;
370       
371       case OP_GONE:
372         printf("OP_GONE: %d\n", buf[0]);
373         u = finduser(buf[0]);
374         sprintf(msgbuf, "client %d (%s) disconnected",
375                 buf[0], get_user_name(buf[0]));
376         sysmsg(msgbuf);
377
378         for (v = &me; v; v = v->next)
379           if (v->next == u)
380             v->next = u->next;
381         free(u);
382
383         break;
384
385       case OP_BADVERS:
386         Error(ERR_RETURN, "protocol version mismatch");
387         Error(ERR_EXIT, "server expects %d.%d.x instead of %d.%d.%d",
388               buf[2], buf[3], PROT_VERS_1, PROT_VERS_2, PROT_VERS_3);
389         break;
390
391       case OP_PLAY:
392         printf("OP_PLAY: %d\n", buf[0]);
393         sprintf(msgbuf, "client %d starts game [level %d from levedir %d (%s)]\n",
394                 buf[0],
395                 (buf[2] << 8) + buf[3],
396                 (buf[4] << 8) + buf[5],
397                 &buf[6]);
398         sysmsg(msgbuf);
399
400         if (strcmp(leveldir[(buf[4] << 8) + buf[5]].name, &buf[6]) == 0)
401         {
402           leveldir_nr = (buf[4] << 8) + buf[5];
403
404           local_player->leveldir_nr = leveldir_nr;
405           LoadPlayerInfo(PLAYER_LEVEL);
406           SavePlayerInfo(PLAYER_SETUP);
407
408           level_nr = (buf[2] << 8) + buf[3];
409
410           TapeErase();
411           LoadLevelTape(level_nr);
412
413           GetPlayerConfig();
414           LoadLevel(level_nr);
415
416           {
417             if (autorecord_on)
418               TapeStartRecording();
419
420             game_status = PLAYING;
421             InitGame();
422           }
423         }
424         else
425         {
426           Error(ERR_RETURN, "no such level directory: '%s'", &buf[6]);
427         }
428         break;
429
430       case OP_MOVE:
431         if (buf[2])
432         {
433           printf("OP_MOVE: %d\n", buf[0]);
434           sprintf(msgbuf, "client %d moves player [0x%02x]", buf[0], buf[2]);
435           sysmsg(msgbuf);
436         }
437         break;
438
439       case OP_PAUSE:
440         printf("OP_PAUSE: %d\n", buf[0]);
441         sprintf(msgbuf, "client %d pauses game", buf[0]);
442         sysmsg(msgbuf);
443         break;
444
445       case OP_CONT:
446         printf("OP_CONT: %d\n", buf[0]);
447         sprintf(msgbuf, "client %d continues game", buf[0]);
448         sysmsg(msgbuf);
449         break;
450
451       case OP_WON:
452         printf("OP_WON: %d\n", buf[0]);
453         sprintf(msgbuf, "client %d wins the game", buf[0]);
454         sysmsg(msgbuf);
455         break;
456
457       case OP_ZERO:
458         printf("OP_ZERO: %d\n", buf[0]);
459         sprintf(msgbuf, "client %d resets game counters", buf[0]);
460         sysmsg(msgbuf);
461         break;
462
463       case OP_MSG:
464         printf("OP_MSG: %d\n", buf[0]);
465         sprintf(msgbuf, "client %d sends message", buf[0]);
466         break;
467       
468       case OP_LOST:
469         printf("OP_MSG: %d\n", buf[0]);
470         sprintf(msgbuf, "client %d has lost", buf[0]);
471         break;
472       
473       case OP_LEVEL:
474         printf("OP_MSG: %d\n", buf[0]);
475         sprintf(msgbuf, "client %d sets level to %d", buf[0], buf[2]);
476         break;
477     }
478   }
479
480   fflush(stdout);
481 }
482
483 void HandleNetworking()
484 {
485   static struct timeval tv = { 0, 0 };
486   fd_set rfds;
487   int r = 0;
488
489   if (standalone)
490     return;
491
492   flushbuf();
493
494   FD_ZERO(&rfds);
495   FD_SET(sfd, &rfds);
496
497   r = select(sfd + 1, &rfds, NULL, NULL, &tv);
498
499   if (r < 0 && errno != EINTR)
500   {
501     perror("select");
502     fatal("fatal: select() failed");
503   }
504
505   if (r < 0)
506     FD_ZERO(&rfds);
507
508   if (FD_ISSET(sfd, &rfds))
509   {
510     int r;
511
512     r = read(sfd, readbuf + nread, BUFLEN - nread);
513
514     if (r < 0)
515       fatal("Error reading from server");
516     if (r == 0)
517       fatal("Connection to server lost");
518     nread += r;
519
520     handlemessages();
521   }
522 }