rnd-20140211-1-src
[rocksndiamonds.git] / src / network.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * network.c                                                *
12 ***********************************************************/
13
14 #include "libgame/platform.h"
15
16 #if defined(NETWORK_AVALIABLE)
17
18 #include <signal.h>
19 #include <sys/time.h>
20
21 #if defined(TARGET_SDL)
22 #include "main.h"
23 #else
24 #include <sys/wait.h>
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <netinet/tcp.h>
28 #include <arpa/inet.h>
29 #include <netdb.h>
30 #endif
31
32 #include "libgame/libgame.h"
33
34 #include "network.h"
35 #include "netserv.h"
36 #include "game.h"
37 #include "tape.h"
38 #include "files.h"
39 #include "tools.h"
40 #include "screens.h"
41
42 struct NetworkClientPlayerInfo
43 {
44   byte nr;
45   char name[MAX_PLAYER_NAME_LEN + 1];
46   struct NetworkClientPlayerInfo *next;
47 };
48
49 static struct NetworkClientPlayerInfo first_player =
50 {
51   0,
52   EMPTY_PLAYER_NAME,
53   NULL
54 };
55
56 /* server stuff */
57
58 #if defined(TARGET_SDL)
59 static TCPsocket sfd;           /* server socket */
60 static SDLNet_SocketSet rfds;   /* socket set */
61 #else
62 static int sfd;                 /* server socket */
63 #endif
64
65 static byte realbuffer[512];
66 static byte readbuffer[MAX_BUFFER_SIZE], writbuffer[MAX_BUFFER_SIZE];
67 static byte *buffer = realbuffer + 4;
68 static int nread = 0, nwrite = 0;
69 static boolean stop_network_game = FALSE;
70
71 static void SendBufferToServer(int size)
72 {
73   if (!options.network)
74     return;
75
76   realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
77   realbuffer[3] = (byte)size;
78   buffer[0] = 0;
79
80   if (nwrite + 4 + size >= MAX_BUFFER_SIZE)
81     Error(ERR_EXIT, "internal error: network send buffer overflow");
82
83   memcpy(writbuffer + nwrite, realbuffer, 4 + size);
84   nwrite += 4 + size;
85
86   /* directly send the buffer to the network server */
87 #if defined(TARGET_SDL)
88   SDLNet_TCP_Send(sfd, writbuffer, nwrite);
89 #else
90   if (write(sfd, writbuffer, nwrite) == -1)
91     Error(ERR_WARN, "write() failed; %s", strerror(errno));
92 #endif
93   nwrite = 0;
94 }
95
96 struct NetworkClientPlayerInfo *getNetworkPlayer(int player_nr)
97 {
98   struct NetworkClientPlayerInfo *player = NULL;
99
100   for (player = &first_player; player; player = player->next)
101     if (player->nr == player_nr)
102       break;
103
104   if (player == NULL)   /* should not happen */
105     Error(ERR_EXIT, "protocol error: reference to non-existing player %d",
106           player_nr);
107
108   return player;
109 }
110
111 char *getNetworkPlayerName(int player_nr)
112 {
113   struct NetworkClientPlayerInfo *player;
114
115   if (player_nr == 0)
116     return("the network game server");
117   else if (player_nr == first_player.nr)
118     return("you");
119   else
120     for (player = &first_player; player; player = player->next)
121       if (player->nr == player_nr && player->name && strlen(player->name))
122         return(player->name);
123
124   return(EMPTY_PLAYER_NAME);
125 }
126
127 static void StartNetworkServer(int port)
128 {
129 #if defined(TARGET_SDL)
130   static int p;
131
132   p = port;
133 #if defined(TARGET_SDL2)
134   server_thread = SDL_CreateThread(NetworkServerThread,
135                                    "NetworkServerThread", &p);
136 #else
137   server_thread = SDL_CreateThread(NetworkServerThread, &p);
138 #endif
139   network_server = TRUE;
140
141 #else
142
143   switch (fork())
144   {
145     case 0:
146       NetworkServer(port, options.serveronly);
147
148       /* never reached */
149       exit(0);
150
151     case -1:
152       Error(ERR_WARN,
153             "cannot create network server process - no network playing");
154       options.network = FALSE;
155       return;
156
157     default:
158       /* we are parent process -- resume normal operation */
159       return;
160   }
161 #endif
162 }
163
164 #if defined(TARGET_SDL)
165 boolean ConnectToServer(char *hostname, int port)
166 {
167   IPaddress ip;
168   int i;
169
170   if (port == 0)
171     port = DEFAULT_SERVER_PORT;
172
173   rfds = SDLNet_AllocSocketSet(1);
174
175   if (hostname)
176   {
177     SDLNet_ResolveHost(&ip, hostname, port);
178     if (ip.host == INADDR_NONE)
179       Error(ERR_EXIT, "cannot locate host '%s'", hostname);
180   }
181   else
182   {
183     SDLNet_Write32(0x7f000001, &ip.host);       /* 127.0.0.1 */
184     SDLNet_Write16(port, &ip.port);
185   }
186
187   sfd = SDLNet_TCP_Open(&ip);
188
189   if (sfd)
190   {
191     SDLNet_TCP_AddSocket(rfds, sfd);
192     return TRUE;
193   }
194   else
195   {
196     printf("SDLNet_TCP_Open(): %s\n", SDLNet_GetError());
197   }
198
199   if (hostname)                 /* connect to specified server failed */
200     return FALSE;
201
202   printf("No rocksndiamonds server on localhost -- starting up one ...\n");
203   StartNetworkServer(port);
204
205   /* wait for server to start up and try connecting several times */
206   for (i = 0; i < 6; i++)
207   {
208     Delay(500);                 /* wait 500 ms == 0.5 seconds */
209
210     if ((sfd = SDLNet_TCP_Open(&ip)))           /* connected */
211     {
212       SDLNet_TCP_AddSocket(rfds, sfd);
213       return TRUE;
214     }
215   }
216
217   /* when reaching this point, connect to newly started server has failed */
218   return FALSE;
219 }
220
221 #else
222
223 boolean ConnectToServer(char *hostname, int port)
224 {
225   struct sockaddr_in s;
226   struct protoent *tcpproto;
227   int on = 1, i;
228
229   if (hostname)
230   {
231     if ((s.sin_addr.s_addr = inet_addr(hostname)) == -1)
232     {
233       struct hostent *host;
234
235       if ((host = gethostbyname(hostname)) == NULL)
236         Error(ERR_EXIT, "cannot locate host '%s'", hostname);
237
238       s.sin_addr = *(struct in_addr *)(host->h_addr_list[0]);
239     }
240   }
241   else
242     s.sin_addr.s_addr = inet_addr("127.0.0.1");         /* localhost */
243
244   if (port == 0)
245     port = DEFAULT_SERVER_PORT;
246
247   s.sin_port = htons(port);
248   s.sin_family = AF_INET;
249
250   sfd = socket(PF_INET, SOCK_STREAM, 0);
251   if (sfd < 0)
252     Error(ERR_EXIT, "out of file descriptors");
253
254   if ((tcpproto = getprotobyname("tcp")) != NULL)
255     setsockopt(sfd, tcpproto->p_proto, TCP_NODELAY, (char *)&on, sizeof(int));
256
257   if (connect(sfd, (struct sockaddr *)&s, sizeof(s)) == 0)      /* connected */
258     return TRUE;
259
260   if (hostname) /* connect to specified server failed */
261     return FALSE;
262
263   printf("No rocksndiamonds server on localhost -- starting up one ...\n");
264   StartNetworkServer(port);
265
266   /* wait for server to start up and try connecting several times */
267   for (i = 0; i < 6; i++)
268   {
269     Delay(500);         /* wait 500 ms == 0.5 seconds */
270     close(sfd);
271
272     sfd = socket(PF_INET, SOCK_STREAM, 0);
273     if (sfd < 0)
274       Error(ERR_EXIT, "out of file descriptors");
275
276     setsockopt(sfd, tcpproto->p_proto, TCP_NODELAY, (char *)&on, sizeof(int));
277
278     if (connect(sfd, (struct sockaddr *)&s, sizeof(s)) >= 0)    /* connected */
279       return TRUE;
280   }
281
282   /* when reaching this point, connect to newly started server has failed */
283   return FALSE;
284 }
285 #endif  /* defined(TARGET_SDL) */
286
287 void SendToServer_PlayerName(char *player_name)
288 {
289   int len_player_name = strlen(player_name);
290
291   buffer[1] = OP_PLAYER_NAME;
292   memcpy(&buffer[2], player_name, len_player_name);
293   SendBufferToServer(2 + len_player_name);
294   Error(ERR_NETWORK_CLIENT, "you set your player name to \"%s\"", player_name);
295 }
296
297 void SendToServer_ProtocolVersion()
298 {
299   buffer[1] = OP_PROTOCOL_VERSION;
300   buffer[2] = PROTOCOL_VERSION_1;
301   buffer[3] = PROTOCOL_VERSION_2;
302   buffer[4] = PROTOCOL_VERSION_3;
303
304   SendBufferToServer(5);
305 }
306
307 void SendToServer_NrWanted(int nr_wanted)
308 {
309   buffer[1] = OP_NUMBER_WANTED;
310   buffer[2] = nr_wanted;
311
312   SendBufferToServer(3);
313 }
314
315 void SendToServer_StartPlaying()
316 {
317   unsigned int new_random_seed = InitRND(level.random_seed);
318
319   int dummy = 0;                /* !!! HAS NO MEANING ANYMORE !!! */
320                                 /* the name of the level must be enough */
321
322   buffer[1] = OP_START_PLAYING;
323   buffer[2] = (byte)(level_nr >> 8);
324   buffer[3] = (byte)(level_nr & 0xff);
325   buffer[4] = (byte)(dummy >> 8);
326   buffer[5] = (byte)(dummy & 0xff);
327
328   buffer[6] = (unsigned char)((new_random_seed >> 24) & 0xff);
329   buffer[7] = (unsigned char)((new_random_seed >> 16) & 0xff);
330   buffer[8] = (unsigned char)((new_random_seed >>  8) & 0xff);
331   buffer[9] = (unsigned char)((new_random_seed >>  0) & 0xff);
332
333   strcpy((char *)&buffer[10], leveldir_current->identifier);
334
335   SendBufferToServer(10 + strlen(leveldir_current->identifier) + 1);
336 }
337
338 void SendToServer_PausePlaying()
339 {
340   buffer[1] = OP_PAUSE_PLAYING;
341
342   SendBufferToServer(2);
343 }
344
345 void SendToServer_ContinuePlaying()
346 {
347   buffer[1] = OP_CONTINUE_PLAYING;
348
349   SendBufferToServer(2);
350 }
351
352 void SendToServer_StopPlaying(int cause_for_stopping)
353 {
354   buffer[1] = OP_STOP_PLAYING;
355   buffer[2] = cause_for_stopping;
356
357   SendBufferToServer(3);
358 }
359
360 void SendToServer_MovePlayer(byte player_action)
361 {
362   buffer[1] = OP_MOVE_PLAYER;
363   buffer[2] = player_action;
364
365   SendBufferToServer(3);
366 }
367
368 static void Handle_OP_BAD_PROTOCOL_VERSION()
369 {
370   Error(ERR_WARN, "protocol version mismatch");
371   Error(ERR_EXIT, "server expects %d.%d.x instead of %d.%d.%d",
372         buffer[2], buffer[3],
373         PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, PROTOCOL_VERSION_3);
374 }
375
376 static void Handle_OP_YOUR_NUMBER()
377 {
378   int new_client_nr = buffer[2];
379   int new_index_nr = new_client_nr - 1;
380   struct PlayerInfo *old_local_player = local_player;
381   struct PlayerInfo *new_local_player = &stored_player[new_index_nr];
382
383   printf("OP_YOUR_NUMBER: %d\n", buffer[0]);
384   first_player.nr = new_client_nr;
385
386   if (old_local_player != new_local_player)
387   {
388     /* copy existing player settings and change to new player */
389
390     *new_local_player = *old_local_player;
391     old_local_player->connected = FALSE;
392     local_player = new_local_player;
393   }
394
395   if (first_player.nr > MAX_PLAYERS)
396     Error(ERR_EXIT, "sorry, more than %d players not allowed", MAX_PLAYERS);
397
398   Error(ERR_NETWORK_CLIENT, "you get client # %d", new_client_nr);
399 }
400
401 static void Handle_OP_NUMBER_WANTED()
402 {
403   int client_nr_wanted = buffer[2];
404   int old_client_nr = buffer[0];
405   int new_client_nr = buffer[3];
406   int old_index_nr = old_client_nr - 1;
407   int new_index_nr = new_client_nr - 1;
408   int index_nr_wanted = client_nr_wanted - 1;
409   struct PlayerInfo *old_player = &stored_player[old_index_nr];
410   struct PlayerInfo *new_player = &stored_player[new_index_nr];
411
412   printf("OP_NUMBER_WANTED: %d\n", buffer[0]);
413
414   if (new_client_nr == client_nr_wanted)        /* switching succeeded */
415   {
416     struct NetworkClientPlayerInfo *player;
417
418     if (old_client_nr != client_nr_wanted)      /* client's nr has changed */
419       Error(ERR_NETWORK_CLIENT, "client %d switches to # %d",
420             old_client_nr, new_client_nr);
421     else if (old_client_nr == first_player.nr)  /* local player keeps his nr */
422       Error(ERR_NETWORK_CLIENT, "keeping client # %d", new_client_nr);
423
424     if (old_client_nr != new_client_nr)
425     {
426       /* copy existing player settings and change to new player */
427
428       *new_player = *old_player;
429       old_player->connected = FALSE;
430     }
431
432     player = getNetworkPlayer(old_client_nr);
433     player->nr = new_client_nr;
434
435     if (old_player == local_player)             /* local player switched */
436       local_player = new_player;
437   }
438   else if (old_client_nr == first_player.nr)    /* failed -- local player? */
439   {
440     char request[100];
441
442     sprintf(request, "Sorry! Player %d already exists! You are player %d!",
443             index_nr_wanted + 1, new_index_nr + 1);
444
445     Request(request, REQ_CONFIRM);
446
447     Error(ERR_NETWORK_CLIENT, "cannot switch -- you keep client # %d",
448           new_client_nr);
449   }
450 }
451
452 static void Handle_OP_PLAYER_NAME(unsigned int len)
453 {
454   struct NetworkClientPlayerInfo *player;
455   int player_nr = (int)buffer[0];
456
457   printf("OP_PLAYER_NAME: %d\n", player_nr);
458   player = getNetworkPlayer(player_nr);
459   buffer[len] = 0;
460   Error(ERR_NETWORK_CLIENT, "client %d calls itself \"%s\"",
461         buffer[0], &buffer[2]);
462   strncpy(player->name, (char *)&buffer[2], MAX_PLAYER_NAME_LEN);
463 }
464
465 static void Handle_OP_PLAYER_CONNECTED()
466 {
467   struct NetworkClientPlayerInfo *player, *last_player = NULL;
468   int new_client_nr = (int)buffer[0];
469   int new_index_nr = new_client_nr - 1;
470
471   printf("OP_PLAYER_CONNECTED: %d\n", new_client_nr);
472   Error(ERR_NETWORK_CLIENT, "new client %d connected", new_client_nr);
473
474   for (player = &first_player; player; player = player->next)
475   {
476     if (player->nr == new_client_nr)
477       Error(ERR_EXIT, "multiplayer server sent duplicate player id");
478
479     last_player = player;
480   }
481
482   last_player->next = player =
483     checked_malloc(sizeof(struct NetworkClientPlayerInfo));
484   player->nr = new_client_nr;
485   player->name[0] = '\0';
486   player->next = NULL;
487
488   stored_player[new_index_nr].connected = TRUE;
489 }
490
491 static void Handle_OP_PLAYER_DISCONNECTED()
492 {
493   struct NetworkClientPlayerInfo *player, *player_disconnected;
494   int player_nr = (int)buffer[0];
495
496   printf("OP_PLAYER_DISCONNECTED: %d\n", player_nr);
497   player_disconnected = getNetworkPlayer(player_nr);
498   Error(ERR_NETWORK_CLIENT, "client %d (%s) disconnected",
499         player_nr, getNetworkPlayerName(buffer[0]));
500
501   for (player = &first_player; player; player = player->next)
502     if (player->next == player_disconnected)
503       player->next = player_disconnected->next;
504   free(player_disconnected);
505 }
506
507 static void Handle_OP_START_PLAYING()
508 {
509   LevelDirTree *new_leveldir;
510   int new_level_nr;
511   unsigned int new_random_seed;
512   char *new_leveldir_identifier;
513
514   new_level_nr = (buffer[2] << 8) + buffer[3];
515   new_random_seed =
516     (buffer[6] << 24) | (buffer[7] << 16) | (buffer[8] << 8) | (buffer[9]);
517   new_leveldir_identifier = (char *)&buffer[10];
518
519   new_leveldir = getTreeInfoFromIdentifier(leveldir_first,
520                                            new_leveldir_identifier);
521   if (new_leveldir == NULL)
522   {
523     Error(ERR_WARN, "no such level identifier: '%s'", new_leveldir_identifier);
524
525     new_leveldir = leveldir_first;
526     Error(ERR_WARN, "using default level set: '%s'", new_leveldir->identifier);
527   }
528
529   printf("OP_START_PLAYING: %d\n", buffer[0]);
530   Error(ERR_NETWORK_CLIENT,
531         "client %d starts game [level %d from level identifier '%s']\n",
532         buffer[0], new_level_nr, new_leveldir->identifier);
533
534   leveldir_current = new_leveldir;
535   level_nr = new_level_nr;
536
537   TapeErase();
538   LoadTape(level_nr);
539   LoadLevel(level_nr);
540
541   StartGameActions(FALSE, setup.autorecord, new_random_seed);
542 }
543
544 static void Handle_OP_PAUSE_PLAYING()
545 {
546   printf("OP_PAUSE_PLAYING: %d\n", buffer[0]);
547   Error(ERR_NETWORK_CLIENT, "client %d pauses game", buffer[0]);
548
549   tape.pausing = TRUE;
550   DrawVideoDisplay(VIDEO_STATE_PAUSE_ON,0);
551 }
552
553 static void Handle_OP_CONTINUE_PLAYING()
554 {
555   printf("OP_CONTINUE_PLAYING: %d\n", buffer[0]);
556   Error(ERR_NETWORK_CLIENT, "client %d continues game", buffer[0]);
557
558   tape.pausing = FALSE;
559   DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
560 }
561
562 static void Handle_OP_STOP_PLAYING()
563 {
564   printf("OP_STOP_PLAYING: %d [%d]\n", buffer[0], buffer[2]);
565   Error(ERR_NETWORK_CLIENT, "client %d stops game [%d]", buffer[0], buffer[2]);
566
567   if (game_status == GAME_MODE_PLAYING)
568   {
569     if (buffer[2] == NETWORK_STOP_BY_PLAYER)
570       Request("Network game stopped by player!", REQ_CONFIRM);
571     else if (buffer[2] == NETWORK_STOP_BY_ERROR)
572       Request("Network game stopped due to internal error!", REQ_CONFIRM);
573     else
574       Request("Network game stopped!", REQ_CONFIRM);
575   }
576
577   game_status = GAME_MODE_MAIN;
578   DrawMainMenu();
579 }
580
581 static void Handle_OP_MOVE_PLAYER(unsigned int len)
582 {
583   int server_frame_counter;
584   int i;
585
586   if (!network_playing)
587     return;
588
589   server_frame_counter =
590     (buffer[2] << 24) | (buffer[3] << 16) | (buffer[4] << 8) | (buffer[5]);
591
592 #if 0
593   Error(ERR_NETWORK_CLIENT, "receiving server frame counter value %d [%d]",
594         server_frame_counter, FrameCounter);
595 #endif
596
597   if (server_frame_counter != FrameCounter)
598   {
599     Error(ERR_INFO, "client and servers frame counters out of sync");
600     Error(ERR_INFO, "frame counter of client is %d", FrameCounter);
601     Error(ERR_INFO, "frame counter of server is %d", server_frame_counter);
602     Error(ERR_INFO, "this should not happen -- please debug");
603
604     stop_network_game = TRUE;
605
606     return;
607   }
608
609   /* copy valid player actions */
610   for (i = 0; i < MAX_PLAYERS; i++)
611     stored_player[i].effective_action =
612       (i < len - 6 && stored_player[i].active ? buffer[6 + i] : 0);
613
614   network_player_action_received = TRUE;
615 }
616
617 static void HandleNetworkingMessages()
618 {
619   unsigned int message_length;
620
621   stop_network_game = FALSE;
622
623   while (nread >= 4 && nread >= 4 + readbuffer[3])
624   {
625     message_length = readbuffer[3];
626     if (readbuffer[0] || readbuffer[1] || readbuffer[2])
627       Error(ERR_EXIT, "wrong network server line length");
628
629     memcpy(buffer, &readbuffer[4], message_length);
630     nread -= 4 + message_length;
631     memmove(readbuffer, readbuffer + 4 + message_length, nread);
632
633     switch (buffer[1])
634     {
635       case OP_BAD_PROTOCOL_VERSION:
636         Handle_OP_BAD_PROTOCOL_VERSION();
637         break;
638
639       case OP_YOUR_NUMBER:
640         Handle_OP_YOUR_NUMBER();
641         break;
642
643       case OP_NUMBER_WANTED:
644         Handle_OP_NUMBER_WANTED();
645         break;
646
647       case OP_PLAYER_NAME:
648         Handle_OP_PLAYER_NAME(message_length);
649         break;
650
651       case OP_PLAYER_CONNECTED:
652         Handle_OP_PLAYER_CONNECTED();
653         break;
654       
655       case OP_PLAYER_DISCONNECTED:
656         Handle_OP_PLAYER_DISCONNECTED();
657         break;
658
659       case OP_START_PLAYING:
660         Handle_OP_START_PLAYING();
661         break;
662
663       case OP_PAUSE_PLAYING:
664         Handle_OP_PAUSE_PLAYING();
665         break;
666
667       case OP_CONTINUE_PLAYING:
668         Handle_OP_CONTINUE_PLAYING();
669         break;
670
671       case OP_STOP_PLAYING:
672         Handle_OP_STOP_PLAYING();
673         break;
674
675       case OP_MOVE_PLAYER:
676         Handle_OP_MOVE_PLAYER(message_length);
677         break;
678
679       case OP_BROADCAST_MESSAGE:
680         printf("OP_BROADCAST_MESSAGE: %d\n", buffer[0]);
681         Error(ERR_NETWORK_CLIENT, "client %d sends message", buffer[0]);
682         break;
683     }
684   }
685
686   fflush(stdout);
687
688   /* in case of internal error, stop network game */
689   if (stop_network_game)
690     SendToServer_StopPlaying(NETWORK_STOP_BY_ERROR);
691 }
692
693 /* TODO */
694
695 void HandleNetworking()
696 {
697 #if !defined(TARGET_SDL)
698   static struct timeval tv = { 0, 0 };
699   fd_set rfds;
700 #endif
701   int r = 0;
702
703   do
704   {
705 #if defined(TARGET_SDL)
706     if ((r = SDLNet_CheckSockets(rfds, 1)) < 0)
707       Error(ERR_EXIT, "HandleNetworking(): SDLNet_CheckSockets() failed");
708
709 #else
710
711     FD_ZERO(&rfds);
712     FD_SET(sfd, &rfds);
713
714     r = select(sfd + 1, &rfds, NULL, NULL, &tv);
715
716     if (r < 0 && errno != EINTR)
717       Error(ERR_EXIT, "HandleNetworking(): select() failed");
718
719     if (r < 0)
720       FD_ZERO(&rfds);
721 #endif
722
723 #if defined(TARGET_SDL)
724     if (r > 0)
725 #else
726     if (FD_ISSET(sfd, &rfds))
727 #endif
728     {
729 #if defined(TARGET_SDL)
730       r = SDLNet_TCP_Recv(sfd, readbuffer + nread, 1);
731 #else
732       r = read(sfd, readbuffer + nread, MAX_BUFFER_SIZE - nread);
733 #endif
734
735       if (r < 0)
736         Error(ERR_EXIT, "error reading from network server");
737
738       if (r == 0)
739         Error(ERR_EXIT, "connection to network server lost");
740
741       nread += r;
742
743       HandleNetworkingMessages();
744     }
745   }
746   while (r > 0);
747 }
748
749 #endif /* PLATFORM_UNIX */