added clearing network players from main menu screen if server disconnects
[rocksndiamonds.git] / src / network.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // network.c
10 // ============================================================================
11
12 #include <signal.h>
13 #include <sys/time.h>
14
15 #include "libgame/libgame.h"
16
17 #include "network.h"
18 #include "netserv.h"
19 #include "main.h"
20 #include "game.h"
21 #include "tape.h"
22 #include "files.h"
23 #include "tools.h"
24 #include "screens.h"
25
26 struct NetworkClientPlayerInfo
27 {
28   byte nr;
29   char name[MAX_PLAYER_NAME_LEN + 1];
30   struct NetworkClientPlayerInfo *next;
31 };
32
33 static struct NetworkClientPlayerInfo first_player =
34 {
35   0,
36   EMPTY_PLAYER_NAME,
37   NULL
38 };
39
40 /* server stuff */
41
42 static TCPsocket sfd;           /* TCP server socket */
43 static UDPsocket udp;           /* UDP server socket */
44 static SDLNet_SocketSet rfds;   /* socket set */
45
46 static byte realbuffer[512];
47 static byte readbuffer[MAX_BUFFER_SIZE], writbuffer[MAX_BUFFER_SIZE];
48 static byte *buffer = realbuffer + 4;
49 static int nread = 0, nwrite = 0;
50 static boolean stop_network_game = FALSE;
51
52 static void DrawNetworkTextExt(char *message, int font_nr, boolean initialize)
53 {
54   static int xpos = 0, ypos = 0;
55   static int max_line_width = 0;
56   int font_width = getFontWidth(font_nr);
57   int font_height = getFontHeight(font_nr);
58   int ypos_1 = 120;
59   int ypos_2 = 150;
60
61   if (initialize)
62   {
63     if (game_status == GAME_MODE_LOADING)
64     {
65       max_line_width = WIN_XSIZE;
66
67       xpos = (max_line_width - getTextWidth(message, font_nr)) / 2;
68       ypos = ypos_1;
69
70       DrawText(xpos, ypos, message, font_nr);
71
72       xpos = 0;
73       ypos = ypos_2;
74     }
75     else
76     {
77       max_line_width = SXSIZE;
78
79       DrawTextSCentered(ypos_1, font_nr, message);
80
81       /* calculate offset to x position caused by rounding */
82       int max_chars_per_line = max_line_width / font_width;
83       int xoffset = (max_line_width - max_chars_per_line * font_width) / 2;
84
85       xpos = SX + xoffset;
86       ypos = SY + ypos_2;
87     }
88
89     Error(ERR_DEBUG, "========== %s ==========", message);
90   }
91   else
92   {
93     int max_chars_per_line = max_line_width / font_width;
94     int max_lines_per_text = 10;
95     int num_lines_spacing = (font_nr == FC_YELLOW ? 1 : 3);
96     int num_lines_printed = DrawTextBuffer(xpos, ypos, message, font_nr,
97                                            max_chars_per_line, -1,
98                                            max_lines_per_text, 0, -1,
99                                            TRUE, TRUE, FALSE);
100
101     ypos += (num_lines_printed + num_lines_spacing) * font_height;
102
103     Error(ERR_DEBUG, "%s", message);
104   }
105
106   BackToFront();
107 }
108
109 static void DrawNetworkText(char *message)
110 {
111   DrawNetworkTextExt(message, FC_YELLOW, FALSE);
112 }
113
114 static void DrawNetworkText_Success(char *message)
115 {
116   DrawNetworkTextExt(message, FC_GREEN, FALSE);
117 }
118
119 static void DrawNetworkText_Failed(char *message)
120 {
121   DrawNetworkTextExt(message, FC_RED, FALSE);
122 }
123
124 static void DrawNetworkText_Title(char *message)
125 {
126   DrawNetworkTextExt(message, FC_GREEN, TRUE);
127 }
128
129 static void SendBufferToServer(int size)
130 {
131   if (!network.enabled)
132     return;
133
134   realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
135   realbuffer[3] = (byte)size;
136   buffer[0] = 0;
137
138   if (nwrite + 4 + size >= MAX_BUFFER_SIZE)
139     Error(ERR_EXIT, "internal error: network send buffer overflow");
140
141   memcpy(writbuffer + nwrite, realbuffer, 4 + size);
142   nwrite += 4 + size;
143
144   /* directly send the buffer to the network server */
145   SDLNet_TCP_Send(sfd, writbuffer, nwrite);
146   nwrite = 0;
147 }
148
149 struct NetworkClientPlayerInfo *getNetworkPlayer(int player_nr)
150 {
151   struct NetworkClientPlayerInfo *player = NULL;
152
153   for (player = &first_player; player; player = player->next)
154     if (player->nr == player_nr)
155       break;
156
157   if (player == NULL)   /* should not happen */
158     Error(ERR_EXIT, "protocol error: reference to non-existing player %d",
159           player_nr);
160
161   return player;
162 }
163
164 char *getNetworkPlayerName(int player_nr)
165 {
166   struct NetworkClientPlayerInfo *player;
167
168   if (player_nr == 0)
169     return("the network game server");
170   else if (player_nr == first_player.nr)
171     return("you");
172   else
173     for (player = &first_player; player; player = player->next)
174       if (player->nr == player_nr && strlen(player->name) > 0)
175         return(player->name);
176
177   return(EMPTY_PLAYER_NAME);
178 }
179
180 static void StartNetworkServer(int port)
181 {
182   static int p;
183
184   p = port;
185 #if defined(TARGET_SDL2)
186   server_thread = SDL_CreateThread(NetworkServerThread,
187                                    "NetworkServerThread", &p);
188 #else
189   server_thread = SDL_CreateThread(NetworkServerThread, &p);
190 #endif
191   network_server = TRUE;
192 }
193
194 boolean ConnectToServer(char *hostname, int port)
195 {
196   IPaddress ip;
197   int server_host = 0;
198   int i;
199
200   DrawNetworkText_Title("Initializing Network");
201
202   if (port == 0)
203     port = DEFAULT_SERVER_PORT;
204
205   if (hostname == NULL)
206   {
207     // if no hostname given, try to auto-detect network server in local network
208     // by doing a UDP broadcast on the network server port and wait for answer
209
210     SDLNet_SocketSet udp_socket_set = SDLNet_AllocSocketSet(1);
211     if (!udp_socket_set)
212       Error(ERR_EXIT, "SDLNet_AllocSocketSet() failed: %s"), SDLNet_GetError();
213
214     udp = SDLNet_UDP_Open(0);
215     if(!udp)
216       Error(ERR_EXIT, "SDLNet_UDP_Open() failed: %s", SDLNet_GetError());
217
218     if (SDLNet_UDP_AddSocket(udp_socket_set, udp) == -1)
219       Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_AddSocket() failed: %s"),
220         SDLNet_GetError();
221
222     char *data_ptr = "network server UDB broadcast";
223     int data_len = strlen(data_ptr) + 1;
224     IPaddress ip_address;
225
226     SDLNet_Write32(0xffffffff, &ip_address.host);       /* 255.255.255.255 */
227     SDLNet_Write16(port,       &ip_address.port);
228
229     UDPpacket packet =
230     {
231       -1,
232       (Uint8 *)data_ptr,
233       data_len,
234       data_len,
235       0,
236       ip_address
237     };
238
239     SDLNet_UDP_Send(udp, -1, &packet);
240
241     DrawNetworkText("Looking for local network server ...");
242
243     /* wait for any local network server to answer UDP broadcast */
244     for (i = 0; i < 5; i++)
245     {
246       if (SDLNet_CheckSockets(udp_socket_set, 0) == 1)
247       {
248         int num_packets = SDLNet_UDP_Recv(udp, &packet);
249
250         if (num_packets == 1)
251         {
252           DrawNetworkText_Success("Network server found!");
253
254           server_host = SDLNet_Read32(&packet.address.host);
255         }
256         else
257         {
258           DrawNetworkText_Failed("No answer from network server!");
259         }
260
261         break;
262       }
263       else
264       {
265         Delay_WithScreenUpdates(100);
266       }
267     }
268
269     if (server_host == 0)
270       DrawNetworkText_Failed("No network server found!");
271   }
272
273   rfds = SDLNet_AllocSocketSet(1);
274
275   if (hostname)
276   {
277     SDLNet_ResolveHost(&ip, hostname, port);
278
279     if (ip.host == INADDR_NONE)
280       Error(ERR_EXIT, "cannot locate host '%s'", hostname);
281     else
282       server_host = SDLNet_Read32(&ip.host);
283
284     DrawNetworkText("Connecting to remote host ...");
285   }
286   else
287   {
288     // if no hostname was given and no network server was auto-detected in the
289     // local network, try to connect to a network server at the local host
290     if (server_host == 0)
291       server_host = 0x7f000001;                 /* 127.0.0.1 */
292
293     SDLNet_Write32(server_host, &ip.host);
294     SDLNet_Write16(port,        &ip.port);
295
296     DrawNetworkText("Connecting to local host ...");
297   }
298
299   Error(ERR_DEBUG, "trying to connect to network server at %d.%d.%d.%d ...",
300         (server_host >> 24) & 0xff,
301         (server_host >> 16) & 0xff,
302         (server_host >>  8) & 0xff,
303         (server_host >>  0) & 0xff);
304
305   sfd = SDLNet_TCP_Open(&ip);
306
307   if (sfd)
308   {
309     SDLNet_TCP_AddSocket(rfds, sfd);
310
311     DrawNetworkText_Success("Successfully connected!");
312
313     return TRUE;
314   }
315   else
316   {
317     DrawNetworkText_Failed("Failed to connect to network server!");
318
319     printf("SDLNet_TCP_Open(): %s\n", SDLNet_GetError());
320   }
321
322   if (hostname)                 /* connect to specified server failed */
323     return FALSE;
324
325   DrawNetworkText("Starting new local network server ...");
326
327   StartNetworkServer(port);
328
329   /* wait for server to start up and try connecting several times */
330   for (i = 0; i < 30; i++)
331   {
332     if ((sfd = SDLNet_TCP_Open(&ip)))           /* connected */
333     {
334       DrawNetworkText_Success("Successfully connected!");
335
336       SDLNet_TCP_AddSocket(rfds, sfd);
337       return TRUE;
338     }
339
340     Delay_WithScreenUpdates(100);
341   }
342
343   DrawNetworkText_Failed("Failed to connect to network server!");
344
345   /* when reaching this point, connect to newly started server has failed */
346   return FALSE;
347 }
348
349 void SendToServer_PlayerName(char *player_name)
350 {
351   int len_player_name = strlen(player_name);
352
353   buffer[1] = OP_PLAYER_NAME;
354   memcpy(&buffer[2], player_name, len_player_name);
355   SendBufferToServer(2 + len_player_name);
356   Error(ERR_NETWORK_CLIENT, "you set your player name to \"%s\"", player_name);
357 }
358
359 void SendToServer_ProtocolVersion()
360 {
361   buffer[1] = OP_PROTOCOL_VERSION;
362   buffer[2] = PROTOCOL_VERSION_1;
363   buffer[3] = PROTOCOL_VERSION_2;
364   buffer[4] = PROTOCOL_VERSION_3;
365
366   SendBufferToServer(5);
367 }
368
369 void SendToServer_NrWanted(int nr_wanted)
370 {
371   buffer[1] = OP_NUMBER_WANTED;
372   buffer[2] = nr_wanted;
373
374   SendBufferToServer(3);
375 }
376
377 void SendToServer_StartPlaying()
378 {
379   unsigned int new_random_seed = InitRND(level.random_seed);
380
381   int dummy = 0;                /* !!! HAS NO MEANING ANYMORE !!! */
382                                 /* the name of the level must be enough */
383
384   buffer[1] = OP_START_PLAYING;
385   buffer[2] = (byte)(level_nr >> 8);
386   buffer[3] = (byte)(level_nr & 0xff);
387   buffer[4] = (byte)(dummy >> 8);
388   buffer[5] = (byte)(dummy & 0xff);
389
390   buffer[6] = (unsigned char)((new_random_seed >> 24) & 0xff);
391   buffer[7] = (unsigned char)((new_random_seed >> 16) & 0xff);
392   buffer[8] = (unsigned char)((new_random_seed >>  8) & 0xff);
393   buffer[9] = (unsigned char)((new_random_seed >>  0) & 0xff);
394
395   strcpy((char *)&buffer[10], leveldir_current->identifier);
396
397   SendBufferToServer(10 + strlen(leveldir_current->identifier) + 1);
398 }
399
400 void SendToServer_PausePlaying()
401 {
402   buffer[1] = OP_PAUSE_PLAYING;
403
404   SendBufferToServer(2);
405 }
406
407 void SendToServer_ContinuePlaying()
408 {
409   buffer[1] = OP_CONTINUE_PLAYING;
410
411   SendBufferToServer(2);
412 }
413
414 void SendToServer_StopPlaying(int cause_for_stopping)
415 {
416   buffer[1] = OP_STOP_PLAYING;
417   buffer[2] = cause_for_stopping;
418
419   SendBufferToServer(3);
420 }
421
422 void SendToServer_MovePlayer(byte player_action)
423 {
424   buffer[1] = OP_MOVE_PLAYER;
425   buffer[2] = player_action;
426
427   SendBufferToServer(3);
428 }
429
430 static void Handle_OP_BAD_PROTOCOL_VERSION()
431 {
432   Error(ERR_WARN, "protocol version mismatch");
433   Error(ERR_EXIT, "server expects %d.%d.x instead of %d.%d.%d",
434         buffer[2], buffer[3],
435         PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, PROTOCOL_VERSION_3);
436 }
437
438 static void Handle_OP_YOUR_NUMBER()
439 {
440   int new_client_nr = buffer[2];
441   int new_index_nr = new_client_nr - 1;
442   struct PlayerInfo *old_local_player = local_player;
443   struct PlayerInfo *new_local_player = &stored_player[new_index_nr];
444
445   printf("OP_YOUR_NUMBER: %d\n", buffer[0]);
446   first_player.nr = new_client_nr;
447
448   if (old_local_player != new_local_player)
449   {
450     /* set relevant player settings and change to new player */
451
452     local_player = new_local_player;
453
454     old_local_player->connected_locally = FALSE;
455     new_local_player->connected_locally = TRUE;
456
457     old_local_player->connected_network = FALSE;
458     new_local_player->connected_network = TRUE;
459   }
460
461   if (first_player.nr > MAX_PLAYERS)
462     Error(ERR_EXIT, "sorry, more than %d players not allowed", MAX_PLAYERS);
463
464   Error(ERR_NETWORK_CLIENT, "you get client # %d", new_client_nr);
465 }
466
467 static void Handle_OP_NUMBER_WANTED()
468 {
469   int client_nr_wanted = buffer[2];
470   int old_client_nr = buffer[0];
471   int new_client_nr = buffer[3];
472   int old_index_nr = old_client_nr - 1;
473   int new_index_nr = new_client_nr - 1;
474   int index_nr_wanted = client_nr_wanted - 1;
475   struct PlayerInfo *old_player = &stored_player[old_index_nr];
476   struct PlayerInfo *new_player = &stored_player[new_index_nr];
477
478   printf("OP_NUMBER_WANTED: %d\n", buffer[0]);
479
480   if (new_client_nr == client_nr_wanted)        /* switching succeeded */
481   {
482     struct NetworkClientPlayerInfo *player;
483
484     if (old_client_nr != client_nr_wanted)      /* client's nr has changed */
485       Error(ERR_NETWORK_CLIENT, "client %d switches to # %d",
486             old_client_nr, new_client_nr);
487     else if (old_client_nr == first_player.nr)  /* local player keeps his nr */
488       Error(ERR_NETWORK_CLIENT, "keeping client # %d", new_client_nr);
489
490     if (old_client_nr != new_client_nr)
491     {
492       /* set relevant player settings and change to new player */
493
494       old_player->connected_network = FALSE;
495       new_player->connected_network = TRUE;
496     }
497
498     player = getNetworkPlayer(old_client_nr);
499     player->nr = new_client_nr;
500
501     if (old_player == local_player)             /* local player switched */
502     {
503       local_player = new_player;
504
505       old_player->connected_locally = FALSE;
506       new_player->connected_locally = TRUE;
507     }
508   }
509   else if (old_client_nr == first_player.nr)    /* failed -- local player? */
510   {
511     char request[100];
512
513     sprintf(request, "Sorry! Player %d already exists! You are player %d!",
514             index_nr_wanted + 1, new_index_nr + 1);
515
516     Request(request, REQ_CONFIRM);
517
518     Error(ERR_NETWORK_CLIENT, "cannot switch -- you keep client # %d",
519           new_client_nr);
520   }
521
522   if (game_status == GAME_MODE_MAIN)
523     DrawNetworkPlayers();
524 }
525
526 static void Handle_OP_PLAYER_NAME(unsigned int len)
527 {
528   struct NetworkClientPlayerInfo *player;
529   int player_nr = (int)buffer[0];
530
531   printf("OP_PLAYER_NAME: %d\n", player_nr);
532   player = getNetworkPlayer(player_nr);
533   buffer[len] = 0;
534   Error(ERR_NETWORK_CLIENT, "client %d calls itself \"%s\"",
535         buffer[0], &buffer[2]);
536   strncpy(player->name, (char *)&buffer[2], MAX_PLAYER_NAME_LEN);
537 }
538
539 static void Handle_OP_PLAYER_CONNECTED()
540 {
541   struct NetworkClientPlayerInfo *player, *last_player = NULL;
542   int new_client_nr = (int)buffer[0];
543   int new_index_nr = new_client_nr - 1;
544
545   printf("OP_PLAYER_CONNECTED: %d\n", new_client_nr);
546   Error(ERR_NETWORK_CLIENT, "new client %d connected", new_client_nr);
547
548   for (player = &first_player; player; player = player->next)
549   {
550     if (player->nr == new_client_nr)
551       Error(ERR_EXIT, "multiplayer server sent duplicate player id");
552
553     last_player = player;
554   }
555
556   last_player->next = player =
557     checked_malloc(sizeof(struct NetworkClientPlayerInfo));
558   player->nr = new_client_nr;
559   player->name[0] = '\0';
560   player->next = NULL;
561
562   stored_player[new_index_nr].connected_network = TRUE;
563 }
564
565 static void Handle_OP_PLAYER_DISCONNECTED()
566 {
567   struct NetworkClientPlayerInfo *player, *player_disconnected;
568   int player_nr = (int)buffer[0];
569   int index_nr = player_nr - 1;
570
571   printf("OP_PLAYER_DISCONNECTED: %d\n", player_nr);
572   player_disconnected = getNetworkPlayer(player_nr);
573   Error(ERR_NETWORK_CLIENT, "client %d (%s) disconnected",
574         player_nr, getNetworkPlayerName(buffer[0]));
575
576   for (player = &first_player; player; player = player->next)
577     if (player->next == player_disconnected)
578       player->next = player_disconnected->next;
579   free(player_disconnected);
580
581   stored_player[index_nr].connected_locally = FALSE;
582   stored_player[index_nr].connected_network = FALSE;
583
584   if (game_status == GAME_MODE_PLAYING)
585   {
586     char message[100];
587
588     sprintf(message, "Player %d left network server! Network game stopped!",
589             player_nr);
590
591     Request(message, REQ_CONFIRM | REQ_STAY_CLOSED);
592
593     SetGameStatus(GAME_MODE_MAIN);
594
595     DrawMainMenu();
596   }
597   else if (game_status == GAME_MODE_MAIN)
598   {
599     DrawNetworkPlayers();
600   }
601 }
602
603 static void Handle_OP_START_PLAYING()
604 {
605   LevelDirTree *new_leveldir;
606   int new_level_nr;
607   unsigned int new_random_seed;
608   char *new_leveldir_identifier;
609
610   new_level_nr = (buffer[2] << 8) + buffer[3];
611   new_random_seed =
612     (buffer[6] << 24) | (buffer[7] << 16) | (buffer[8] << 8) | (buffer[9]);
613   new_leveldir_identifier = (char *)&buffer[10];
614
615   new_leveldir = getTreeInfoFromIdentifier(leveldir_first,
616                                            new_leveldir_identifier);
617   if (new_leveldir == NULL)
618   {
619     Error(ERR_WARN, "no such level identifier: '%s'", new_leveldir_identifier);
620
621     new_leveldir = leveldir_first;
622     Error(ERR_WARN, "using default level set: '%s'", new_leveldir->identifier);
623   }
624
625   printf("OP_START_PLAYING: %d\n", buffer[0]);
626   Error(ERR_NETWORK_CLIENT,
627         "client %d starts game [level %d from level identifier '%s']\n",
628         buffer[0], new_level_nr, new_leveldir->identifier);
629
630   leveldir_current = new_leveldir;
631   level_nr = new_level_nr;
632
633   TapeErase();
634   LoadTape(level_nr);
635   LoadLevel(level_nr);
636
637   StartGameActions(FALSE, setup.autorecord, new_random_seed);
638 }
639
640 static void Handle_OP_PAUSE_PLAYING()
641 {
642   printf("OP_PAUSE_PLAYING: %d\n", buffer[0]);
643   Error(ERR_NETWORK_CLIENT, "client %d pauses game", buffer[0]);
644
645   tape.pausing = TRUE;
646   DrawVideoDisplay(VIDEO_STATE_PAUSE_ON,0);
647 }
648
649 static void Handle_OP_CONTINUE_PLAYING()
650 {
651   printf("OP_CONTINUE_PLAYING: %d\n", buffer[0]);
652   Error(ERR_NETWORK_CLIENT, "client %d continues game", buffer[0]);
653
654   tape.pausing = FALSE;
655   DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
656 }
657
658 static void Handle_OP_STOP_PLAYING()
659 {
660   printf("OP_STOP_PLAYING: %d [%d]\n", buffer[0], buffer[2]);
661   Error(ERR_NETWORK_CLIENT, "client %d stops game [%d]", buffer[0], buffer[2]);
662
663   if (game_status == GAME_MODE_PLAYING)
664   {
665     int client_nr = buffer[0];
666     int index_nr = client_nr - 1;
667     struct PlayerInfo *client_player = &stored_player[index_nr];
668     boolean stopped_by_remote_player = (!client_player->connected_locally);
669     char message[100];
670
671     sprintf(message, (buffer[2] == NETWORK_STOP_BY_PLAYER ?
672                       "Network game stopped by player %d!" :
673                       buffer[2] == NETWORK_STOP_BY_ERROR ?
674                       "Network game stopped due to internal error!" :
675                       "Network game stopped!"), client_nr);
676
677     if (buffer[2] != NETWORK_STOP_BY_PLAYER || stopped_by_remote_player)
678       Request(message, REQ_CONFIRM | REQ_STAY_CLOSED);
679
680     SetGameStatus(GAME_MODE_MAIN);
681
682     DrawMainMenu();
683   }
684 }
685
686 static void Handle_OP_MOVE_PLAYER(unsigned int len)
687 {
688   int server_frame_counter;
689   int i;
690
691   if (!network_playing)
692     return;
693
694   server_frame_counter =
695     (buffer[2] << 24) | (buffer[3] << 16) | (buffer[4] << 8) | (buffer[5]);
696
697   if (server_frame_counter != FrameCounter)
698   {
699     Error(ERR_INFO, "client and servers frame counters out of sync");
700     Error(ERR_INFO, "frame counter of client is %d", FrameCounter);
701     Error(ERR_INFO, "frame counter of server is %d", server_frame_counter);
702     Error(ERR_INFO, "this should not happen -- please debug");
703
704     stop_network_game = TRUE;
705
706     return;
707   }
708
709   /* copy valid player actions */
710   for (i = 0; i < MAX_PLAYERS; i++)
711     stored_player[i].effective_action =
712       (i < len - 6 ? buffer[6 + i] : 0);
713
714   network_player_action_received = TRUE;
715 }
716
717 static void HandleNetworkingMessages()
718 {
719   unsigned int message_length;
720
721   stop_network_game = FALSE;
722
723   while (nread >= 4 && nread >= 4 + readbuffer[3])
724   {
725     message_length = readbuffer[3];
726     if (readbuffer[0] || readbuffer[1] || readbuffer[2])
727       Error(ERR_EXIT, "wrong network server line length");
728
729     memcpy(buffer, &readbuffer[4], message_length);
730     nread -= 4 + message_length;
731     memmove(readbuffer, readbuffer + 4 + message_length, nread);
732
733     switch (buffer[1])
734     {
735       case OP_BAD_PROTOCOL_VERSION:
736         Handle_OP_BAD_PROTOCOL_VERSION();
737         break;
738
739       case OP_YOUR_NUMBER:
740         Handle_OP_YOUR_NUMBER();
741         break;
742
743       case OP_NUMBER_WANTED:
744         Handle_OP_NUMBER_WANTED();
745         break;
746
747       case OP_PLAYER_NAME:
748         Handle_OP_PLAYER_NAME(message_length);
749         break;
750
751       case OP_PLAYER_CONNECTED:
752         Handle_OP_PLAYER_CONNECTED();
753         break;
754       
755       case OP_PLAYER_DISCONNECTED:
756         Handle_OP_PLAYER_DISCONNECTED();
757         break;
758
759       case OP_START_PLAYING:
760         Handle_OP_START_PLAYING();
761         break;
762
763       case OP_PAUSE_PLAYING:
764         Handle_OP_PAUSE_PLAYING();
765         break;
766
767       case OP_CONTINUE_PLAYING:
768         Handle_OP_CONTINUE_PLAYING();
769         break;
770
771       case OP_STOP_PLAYING:
772         Handle_OP_STOP_PLAYING();
773         break;
774
775       case OP_MOVE_PLAYER:
776         Handle_OP_MOVE_PLAYER(message_length);
777         break;
778
779       case OP_BROADCAST_MESSAGE:
780         printf("OP_BROADCAST_MESSAGE: %d\n", buffer[0]);
781         Error(ERR_NETWORK_CLIENT, "client %d sends message", buffer[0]);
782         break;
783     }
784   }
785
786   fflush(stdout);
787
788   /* in case of internal error, stop network game */
789   if (stop_network_game)
790     SendToServer_StopPlaying(NETWORK_STOP_BY_ERROR);
791 }
792
793 static char *HandleNetworkingPackets()
794 {
795   while (1)
796   {
797     /* ---------- check network server for activity ---------- */
798
799     int num_active_sockets = SDLNet_CheckSockets(rfds, 1);
800
801     if (num_active_sockets < 0)
802       return "Error checking network sockets!";
803
804     if (num_active_sockets == 0)
805       break;    // no active sockets, stop here
806
807     /* ---------- read packets from network server ---------- */
808
809     int num_bytes = SDLNet_TCP_Recv(sfd, readbuffer + nread, 1);
810
811     if (num_bytes < 0)
812       return "Error reading from network server!";
813
814     if (num_bytes == 0)
815       return "Connection to network server lost!";
816
817     nread += num_bytes;
818
819     HandleNetworkingMessages();
820   }
821
822   return NULL;
823 }
824
825 static void HandleNetworkingDisconnect()
826 {
827   int i;
828
829   SDLNet_TCP_DelSocket(rfds, sfd);
830   SDLNet_TCP_Close(sfd);
831
832   network_playing = FALSE;
833
834   network.enabled = FALSE;
835   network.connected = FALSE;
836
837   setup.network_mode = FALSE;
838
839   for (i = 0; i < MAX_PLAYERS; i++)
840     stored_player[i].connected_network = FALSE;
841 }
842
843 void HandleNetworking()
844 {
845   char *error_message = HandleNetworkingPackets();
846
847   if (error_message != NULL)
848   {
849     HandleNetworkingDisconnect();
850
851     if (game_status == GAME_MODE_PLAYING)
852     {
853       Request(error_message, REQ_CONFIRM | REQ_STAY_CLOSED);
854
855       SetGameStatus(GAME_MODE_MAIN);
856
857       DrawMainMenu();
858     }
859     else
860     {
861       Request(error_message, REQ_CONFIRM);
862
863       if (game_status == GAME_MODE_MAIN)
864         ClearNetworkPlayers();
865     }
866   }
867 }
868
869 void DisconnectFromNetworkServer()
870 {
871   DrawNetworkText_Title("Terminating Network");
872   DrawNetworkText("Disconnecting from network server ...");
873
874   HandleNetworkingDisconnect();
875
876   DrawNetworkText_Success("Successfully disconnected!");
877
878   /* short time to recognize result of network initialization */
879   Delay_WithScreenUpdates(1000);
880 }