rnd-19981001-1
[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
30 int norestart = 0;
31 int nospeedup = 0;
32
33 #define DEFAULTPORT 19503
34
35 #define PROT_VERS_1 1
36 #define PROT_VERS_2 0
37 #define PROT_VERS_3 1
38
39 #define OP_NICK 1
40 #define OP_PLAY 2
41 #define OP_FALL 3
42 #define OP_DRAW 4
43 #define OP_LOST 5
44 #define OP_GONE 6
45 #define OP_CLEAR 7
46 #define OP_NEW 8
47 #define OP_LINES 9
48 #define OP_GROW 10
49 #define OP_MODE 11
50 #define OP_LEVEL 12
51 #define OP_BOT 13
52 #define OP_KILL 14
53 #define OP_PAUSE 15
54 #define OP_CONT 16
55 #define OP_VERSION 17
56 #define OP_BADVERS 18
57 #define OP_MSG 19
58 #define OP_YOUARE 20
59 #define OP_LINESTO 21
60 #define OP_WON 22
61 #define OP_ZERO 23
62
63 /* server stuff */
64
65 #define BUFLEN          4096
66
67 int sfd;
68 unsigned char realbuf[512], readbuf[BUFLEN], writbuf[BUFLEN];
69 unsigned char *buf = realbuf + 4;
70 int nread = 0, nwrite = 0;
71
72 /* like memcpy, but guaranteed to handle overlap when s <= t */
73 void copydown(char *s, char *t, int n)
74 {
75   for (; n; n--)
76     *(s++) = *(t++);
77 }
78
79 void sysmsg(char *s)
80 {
81   printf("** %s\n", s);
82   fflush(stdout);
83 }
84
85 void fatal(char *s)
86 {
87   fprintf(stderr, "%s.\n", s);
88   exit(1);
89 }
90
91 void u_sleep(int i)
92 {
93   struct timeval tm;
94   tm.tv_sec = i / 1000000;
95   tm.tv_usec = i % 1000000;
96   select(0, NULL, NULL, NULL, &tm);
97 }
98
99 void flushbuf()
100 {
101   if (nwrite)
102   {
103     write(sfd, writbuf, nwrite);
104     nwrite = 0;
105   }
106 }
107
108 void sendbuf(int len)
109 {
110   if (!standalone)
111   {
112     realbuf[0] = realbuf[1] = realbuf[2] = 0;
113     realbuf[3] = (unsigned char)len;
114     buf[0] = 0;
115     if (nwrite + 4 + len >= BUFLEN)
116       fatal("Internal error: send buffer overflow");
117     memcpy(writbuf + nwrite, realbuf, 4 + len);
118     nwrite += 4 + len;
119   }
120 }
121
122 void startserver()
123 {
124   char *options[2];
125   int n = 0;
126
127   options[0] = options[1] = NULL;
128   if (norestart)
129     options[n++] = "-norestart";
130   if (nospeedup)
131     options[n++] = "-nospeedup";
132
133   switch (fork())
134   {
135     case 0:
136       execlp(
137 #ifdef XTRISPATH
138       XTRISPATH "/rnd_server",
139 #else
140       "rnd_server",
141 #endif
142       "rnd_server", "-once", "-v", options[0], options[1], NULL);
143
144       fprintf(stderr, "Can't start server '%s'.\n",
145 #ifdef XTRISPATH
146         XTRISPATH "/rnd_server"
147 #else
148         "rnd_server"
149 #endif
150               );
151
152       _exit(1);
153     
154     case -1:
155       fatal("fork() failed");
156     
157     default:
158       return;
159   }
160 }
161
162 BOOL ConnectToServer(char *host, int port)
163 {
164   struct hostent *hp;
165   struct sockaddr_in s;
166   struct protoent *tcpproto;
167   int on = 1, i;
168
169   if (host)
170   {
171     if ((s.sin_addr.s_addr = inet_addr(host)) == -1)
172     {
173       hp = gethostbyname(host);
174       if (!hp)
175         fatal("Host not found");
176       s.sin_addr = *(struct in_addr *)(hp->h_addr_list[0]);
177     }
178   }
179   else
180     s.sin_addr.s_addr = inet_addr("127.0.0.1");
181
182   if (port == 0)
183     port = DEFAULTPORT;
184
185   s.sin_port = htons(port);
186   s.sin_family = AF_INET;
187   sfd = socket(PF_INET, SOCK_STREAM, 0);
188   if (sfd < 0)
189     fatal("Out of file descriptors");
190   if ((tcpproto = getprotobyname("tcp")) != NULL)
191     setsockopt(sfd, tcpproto->p_proto, TCP_NODELAY, (char *)&on, sizeof(int));
192
193   if (connect(sfd, (struct sockaddr *)&s, sizeof(s)) < 0)
194   {
195     if (!host)
196     {
197       printf("No rocksndiamonds server on localhost - starting up one ...\n");
198       startserver();
199       for (i=0; i<6; i++)
200       {
201         u_sleep(500000);
202         close(sfd);
203         sfd = socket(PF_INET, SOCK_STREAM, 0);
204         if (sfd < 0)
205           fatal("Out of file descriptors");
206         setsockopt(sfd, tcpproto->p_proto, TCP_NODELAY, (char *)&on, sizeof(int));
207         if (connect(sfd, (struct sockaddr *)&s, sizeof(s)) >= 0)
208           break;
209       }
210       if (i==6)
211         fatal("Can't connect to server");
212     }
213     else
214       fatal("Can't connect to server");
215   }
216
217   return(TRUE);
218 }
219
220 void SendNicknameToServer(char *nickname)
221 {
222   static char msgbuf[300];
223
224   buf[1] = OP_NICK;
225   memcpy(&buf[2], nickname, strlen(nickname));
226   sendbuf(2 + strlen(nickname));
227   sprintf(msgbuf, "you set your nick to \"%s\"", nickname);
228   sysmsg(msgbuf);
229 }
230
231 void SendProtocolVersionToServer()
232 {
233   buf[1] = OP_VERSION;
234   buf[2] = PROT_VERS_1;
235   buf[3] = PROT_VERS_2;
236   buf[4] = PROT_VERS_3;
237   sendbuf(5);
238 }
239
240 void handlemessages()
241 {
242   unsigned int len;
243   static char msgbuf[300];
244
245   while (nread >= 4 && nread >= 4 + readbuf[3])
246   {
247     len = readbuf[3];
248     if (readbuf[0] || readbuf[1] || readbuf[2])
249       fatal("Wrong server line length");
250
251     memcpy(buf, &readbuf[4], len);
252     nread -= 4 + len;
253     copydown(readbuf, readbuf + 4 + len, nread);
254
255     switch(buf[1])
256     {
257       case OP_YOUARE:
258         printf("OP_YOUARE: %d\n", buf[0]);
259         break;
260
261       case OP_NEW:
262         printf("OP_NEW: %d\n", buf[0]);
263         sprintf(msgbuf, "new client %d connected", buf[0]);
264         sysmsg(msgbuf);
265         break;
266       
267       case OP_GONE:
268         printf("OP_GONE: %d\n", buf[0]);
269         sprintf(msgbuf, "client %d disconnected", buf[0]);
270         sysmsg(msgbuf);
271         break;
272
273       case OP_BADVERS:
274         {
275           static char tmpbuf[128];
276
277           sprintf(tmpbuf, "Protocol version mismatch: server expects %d.%d.x instead of %d.%d.%d\n", buf[2], buf[3], PROT_VERS_1, PROT_VERS_2, PROT_VERS_3);
278           fatal(tmpbuf);
279         }
280         break;
281       
282       case OP_PLAY:
283         printf("OP_PLAY: %d\n", buf[0]);
284         sprintf(msgbuf, "client %d starts game", buf[0]);
285         sysmsg(msgbuf);
286         break;
287
288       case OP_PAUSE:
289         printf("OP_PAUSE: %d\n", buf[0]);
290         sprintf(msgbuf, "client %d pauses game", buf[0]);
291         sysmsg(msgbuf);
292         break;
293
294       case OP_CONT:
295         printf("OP_CONT: %d\n", buf[0]);
296         sprintf(msgbuf, "client %d continues game", buf[0]);
297         sysmsg(msgbuf);
298         break;
299
300       case OP_WON:
301         printf("OP_WON: %d\n", buf[0]);
302         sprintf(msgbuf, "client %d wins the game", buf[0]);
303         sysmsg(msgbuf);
304         break;
305
306       case OP_ZERO:
307         printf("OP_ZERO: %d\n", buf[0]);
308         sprintf(msgbuf, "client %d resets game counters", buf[0]);
309         sysmsg(msgbuf);
310         break;
311
312       case OP_NICK:
313         printf("OP_NICK: %d\n", buf[0]);
314         sprintf(msgbuf, "client %d calls itself \"%s\"", buf[0], &buf[2]);
315         sysmsg(msgbuf);
316         break;
317
318       case OP_MSG:
319         printf("OP_MSG: %d\n", buf[0]);
320         sprintf(msgbuf, "client %d sends message", buf[0]);
321         break;
322       
323       case OP_LOST:
324         printf("OP_MSG: %d\n", buf[0]);
325         sprintf(msgbuf, "client %d has lost", buf[0]);
326         break;
327       
328       case OP_LEVEL:
329         printf("OP_MSG: %d\n", buf[0]);
330         sprintf(msgbuf, "client %d sets level to %d", buf[0], buf[2]);
331         break;
332     }
333   }
334
335   fflush(stdout);
336 }
337
338 void HandleNetworking()
339 {
340   static struct timeval tv = { 0, 0 };
341   fd_set rfds;
342   int r = 0;
343
344   if (standalone)
345     return;
346
347   flushbuf();
348
349   FD_ZERO(&rfds);
350   FD_SET(sfd, &rfds);
351
352   r = select(sfd + 1, &rfds, NULL, NULL, &tv);
353
354   if (r < 0 && errno != EINTR)
355   {
356     perror("select");
357     fatal("fatal: select() failed");
358   }
359
360   if (r < 0)
361     FD_ZERO(&rfds);
362
363   if (FD_ISSET(sfd, &rfds))
364   {
365     int r;
366
367     r = read(sfd, readbuf + nread, BUFLEN - nread);
368
369     if (r < 0)
370       fatal("Error reading from server");
371     if (r == 0)
372       fatal("Connection to server lost");
373     nread += r;
374
375     handlemessages();
376   }
377 }