changed drawing to playfield when changing network mode setup options
[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     if (SDLNet_CheckSockets(udp_socket_set, 500) == 1)
244     {
245       int num_packets = SDLNet_UDP_Recv(udp, &packet);
246
247       if (num_packets == 1)
248       {
249         DrawNetworkText_Success("Network server found!");
250
251         server_host = SDLNet_Read32(&packet.address.host);
252       }
253       else
254       {
255         DrawNetworkText_Failed("No answer from network server!");
256       }
257     }
258     else
259     {
260       DrawNetworkText_Failed("No network server found!");
261     }
262   }
263
264   rfds = SDLNet_AllocSocketSet(1);
265
266   if (hostname)
267   {
268     SDLNet_ResolveHost(&ip, hostname, port);
269
270     if (ip.host == INADDR_NONE)
271       Error(ERR_EXIT, "cannot locate host '%s'", hostname);
272     else
273       server_host = SDLNet_Read32(&ip.host);
274
275     DrawNetworkText("Connecting to remote host ...");
276   }
277   else
278   {
279     // if no hostname was given and no network server was auto-detected in the
280     // local network, try to connect to a network server at the local host
281     if (server_host == 0)
282       server_host = 0x7f000001;                 /* 127.0.0.1 */
283
284     SDLNet_Write32(server_host, &ip.host);
285     SDLNet_Write16(port,        &ip.port);
286
287     DrawNetworkText("Connecting to local host ...");
288   }
289
290   Error(ERR_DEBUG, "trying to connect to network server at %d.%d.%d.%d ...",
291         (server_host >> 24) & 0xff,
292         (server_host >> 16) & 0xff,
293         (server_host >>  8) & 0xff,
294         (server_host >>  0) & 0xff);
295
296   sfd = SDLNet_TCP_Open(&ip);
297
298   if (sfd)
299   {
300     SDLNet_TCP_AddSocket(rfds, sfd);
301
302     DrawNetworkText_Success("Successfully connected!");
303
304     return TRUE;
305   }
306   else
307   {
308     DrawNetworkText_Failed("Failed to connect to network server!");
309
310     printf("SDLNet_TCP_Open(): %s\n", SDLNet_GetError());
311   }
312
313   if (hostname)                 /* connect to specified server failed */
314     return FALSE;
315
316   DrawNetworkText("Starting new local network server ...");
317
318   StartNetworkServer(port);
319
320   /* wait for server to start up and try connecting several times */
321   for (i = 0; i < 30; i++)
322   {
323     if ((sfd = SDLNet_TCP_Open(&ip)))           /* connected */
324     {
325       DrawNetworkText_Success("Successfully connected!");
326
327       SDLNet_TCP_AddSocket(rfds, sfd);
328       return TRUE;
329     }
330
331     Delay(100);
332   }
333
334   DrawNetworkText_Failed("Failed to connect to network server!");
335
336   /* when reaching this point, connect to newly started server has failed */
337   return FALSE;
338 }
339
340 void SendToServer_PlayerName(char *player_name)
341 {
342   int len_player_name = strlen(player_name);
343
344   buffer[1] = OP_PLAYER_NAME;
345   memcpy(&buffer[2], player_name, len_player_name);
346   SendBufferToServer(2 + len_player_name);
347   Error(ERR_NETWORK_CLIENT, "you set your player name to \"%s\"", player_name);
348 }
349
350 void SendToServer_ProtocolVersion()
351 {
352   buffer[1] = OP_PROTOCOL_VERSION;
353   buffer[2] = PROTOCOL_VERSION_1;
354   buffer[3] = PROTOCOL_VERSION_2;
355   buffer[4] = PROTOCOL_VERSION_3;
356
357   SendBufferToServer(5);
358 }
359
360 void SendToServer_NrWanted(int nr_wanted)
361 {
362   buffer[1] = OP_NUMBER_WANTED;
363   buffer[2] = nr_wanted;
364
365   SendBufferToServer(3);
366 }
367
368 void SendToServer_StartPlaying()
369 {
370   unsigned int new_random_seed = InitRND(level.random_seed);
371
372   int dummy = 0;                /* !!! HAS NO MEANING ANYMORE !!! */
373                                 /* the name of the level must be enough */
374
375   buffer[1] = OP_START_PLAYING;
376   buffer[2] = (byte)(level_nr >> 8);
377   buffer[3] = (byte)(level_nr & 0xff);
378   buffer[4] = (byte)(dummy >> 8);
379   buffer[5] = (byte)(dummy & 0xff);
380
381   buffer[6] = (unsigned char)((new_random_seed >> 24) & 0xff);
382   buffer[7] = (unsigned char)((new_random_seed >> 16) & 0xff);
383   buffer[8] = (unsigned char)((new_random_seed >>  8) & 0xff);
384   buffer[9] = (unsigned char)((new_random_seed >>  0) & 0xff);
385
386   strcpy((char *)&buffer[10], leveldir_current->identifier);
387
388   SendBufferToServer(10 + strlen(leveldir_current->identifier) + 1);
389 }
390
391 void SendToServer_PausePlaying()
392 {
393   buffer[1] = OP_PAUSE_PLAYING;
394
395   SendBufferToServer(2);
396 }
397
398 void SendToServer_ContinuePlaying()
399 {
400   buffer[1] = OP_CONTINUE_PLAYING;
401
402   SendBufferToServer(2);
403 }
404
405 void SendToServer_StopPlaying(int cause_for_stopping)
406 {
407   buffer[1] = OP_STOP_PLAYING;
408   buffer[2] = cause_for_stopping;
409
410   SendBufferToServer(3);
411 }
412
413 void SendToServer_MovePlayer(byte player_action)
414 {
415   buffer[1] = OP_MOVE_PLAYER;
416   buffer[2] = player_action;
417
418   SendBufferToServer(3);
419 }
420
421 static void Handle_OP_BAD_PROTOCOL_VERSION()
422 {
423   Error(ERR_WARN, "protocol version mismatch");
424   Error(ERR_EXIT, "server expects %d.%d.x instead of %d.%d.%d",
425         buffer[2], buffer[3],
426         PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, PROTOCOL_VERSION_3);
427 }
428
429 static void Handle_OP_YOUR_NUMBER()
430 {
431   int new_client_nr = buffer[2];
432   int new_index_nr = new_client_nr - 1;
433   struct PlayerInfo *old_local_player = local_player;
434   struct PlayerInfo *new_local_player = &stored_player[new_index_nr];
435
436   printf("OP_YOUR_NUMBER: %d\n", buffer[0]);
437   first_player.nr = new_client_nr;
438
439   if (old_local_player != new_local_player)
440   {
441     /* set relevant player settings and change to new player */
442
443     local_player = new_local_player;
444
445     old_local_player->connected_locally = FALSE;
446     new_local_player->connected_locally = TRUE;
447
448     old_local_player->connected_network = FALSE;
449     new_local_player->connected_network = TRUE;
450   }
451
452   if (first_player.nr > MAX_PLAYERS)
453     Error(ERR_EXIT, "sorry, more than %d players not allowed", MAX_PLAYERS);
454
455   Error(ERR_NETWORK_CLIENT, "you get client # %d", new_client_nr);
456 }
457
458 static void Handle_OP_NUMBER_WANTED()
459 {
460   int client_nr_wanted = buffer[2];
461   int old_client_nr = buffer[0];
462   int new_client_nr = buffer[3];
463   int old_index_nr = old_client_nr - 1;
464   int new_index_nr = new_client_nr - 1;
465   int index_nr_wanted = client_nr_wanted - 1;
466   struct PlayerInfo *old_player = &stored_player[old_index_nr];
467   struct PlayerInfo *new_player = &stored_player[new_index_nr];
468
469   printf("OP_NUMBER_WANTED: %d\n", buffer[0]);
470
471   if (new_client_nr == client_nr_wanted)        /* switching succeeded */
472   {
473     struct NetworkClientPlayerInfo *player;
474
475     if (old_client_nr != client_nr_wanted)      /* client's nr has changed */
476       Error(ERR_NETWORK_CLIENT, "client %d switches to # %d",
477             old_client_nr, new_client_nr);
478     else if (old_client_nr == first_player.nr)  /* local player keeps his nr */
479       Error(ERR_NETWORK_CLIENT, "keeping client # %d", new_client_nr);
480
481     if (old_client_nr != new_client_nr)
482     {
483       /* set relevant player settings and change to new player */
484
485       old_player->connected_network = FALSE;
486       new_player->connected_network = TRUE;
487     }
488
489     player = getNetworkPlayer(old_client_nr);
490     player->nr = new_client_nr;
491
492     if (old_player == local_player)             /* local player switched */
493     {
494       local_player = new_player;
495
496       old_player->connected_locally = FALSE;
497       new_player->connected_locally = TRUE;
498     }
499   }
500   else if (old_client_nr == first_player.nr)    /* failed -- local player? */
501   {
502     char request[100];
503
504     sprintf(request, "Sorry! Player %d already exists! You are player %d!",
505             index_nr_wanted + 1, new_index_nr + 1);
506
507     Request(request, REQ_CONFIRM);
508
509     Error(ERR_NETWORK_CLIENT, "cannot switch -- you keep client # %d",
510           new_client_nr);
511   }
512 }
513
514 static void Handle_OP_PLAYER_NAME(unsigned int len)
515 {
516   struct NetworkClientPlayerInfo *player;
517   int player_nr = (int)buffer[0];
518
519   printf("OP_PLAYER_NAME: %d\n", player_nr);
520   player = getNetworkPlayer(player_nr);
521   buffer[len] = 0;
522   Error(ERR_NETWORK_CLIENT, "client %d calls itself \"%s\"",
523         buffer[0], &buffer[2]);
524   strncpy(player->name, (char *)&buffer[2], MAX_PLAYER_NAME_LEN);
525 }
526
527 static void Handle_OP_PLAYER_CONNECTED()
528 {
529   struct NetworkClientPlayerInfo *player, *last_player = NULL;
530   int new_client_nr = (int)buffer[0];
531   int new_index_nr = new_client_nr - 1;
532
533   printf("OP_PLAYER_CONNECTED: %d\n", new_client_nr);
534   Error(ERR_NETWORK_CLIENT, "new client %d connected", new_client_nr);
535
536   for (player = &first_player; player; player = player->next)
537   {
538     if (player->nr == new_client_nr)
539       Error(ERR_EXIT, "multiplayer server sent duplicate player id");
540
541     last_player = player;
542   }
543
544   last_player->next = player =
545     checked_malloc(sizeof(struct NetworkClientPlayerInfo));
546   player->nr = new_client_nr;
547   player->name[0] = '\0';
548   player->next = NULL;
549
550   stored_player[new_index_nr].connected_network = TRUE;
551 }
552
553 static void Handle_OP_PLAYER_DISCONNECTED()
554 {
555   struct NetworkClientPlayerInfo *player, *player_disconnected;
556   int player_nr = (int)buffer[0];
557   int index_nr = player_nr - 1;
558
559   printf("OP_PLAYER_DISCONNECTED: %d\n", player_nr);
560   player_disconnected = getNetworkPlayer(player_nr);
561   Error(ERR_NETWORK_CLIENT, "client %d (%s) disconnected",
562         player_nr, getNetworkPlayerName(buffer[0]));
563
564   for (player = &first_player; player; player = player->next)
565     if (player->next == player_disconnected)
566       player->next = player_disconnected->next;
567   free(player_disconnected);
568
569   stored_player[index_nr].connected_locally = FALSE;
570   stored_player[index_nr].connected_network = FALSE;
571 }
572
573 static void Handle_OP_START_PLAYING()
574 {
575   LevelDirTree *new_leveldir;
576   int new_level_nr;
577   unsigned int new_random_seed;
578   char *new_leveldir_identifier;
579
580   new_level_nr = (buffer[2] << 8) + buffer[3];
581   new_random_seed =
582     (buffer[6] << 24) | (buffer[7] << 16) | (buffer[8] << 8) | (buffer[9]);
583   new_leveldir_identifier = (char *)&buffer[10];
584
585   new_leveldir = getTreeInfoFromIdentifier(leveldir_first,
586                                            new_leveldir_identifier);
587   if (new_leveldir == NULL)
588   {
589     Error(ERR_WARN, "no such level identifier: '%s'", new_leveldir_identifier);
590
591     new_leveldir = leveldir_first;
592     Error(ERR_WARN, "using default level set: '%s'", new_leveldir->identifier);
593   }
594
595   printf("OP_START_PLAYING: %d\n", buffer[0]);
596   Error(ERR_NETWORK_CLIENT,
597         "client %d starts game [level %d from level identifier '%s']\n",
598         buffer[0], new_level_nr, new_leveldir->identifier);
599
600   leveldir_current = new_leveldir;
601   level_nr = new_level_nr;
602
603   TapeErase();
604   LoadTape(level_nr);
605   LoadLevel(level_nr);
606
607   StartGameActions(FALSE, setup.autorecord, new_random_seed);
608 }
609
610 static void Handle_OP_PAUSE_PLAYING()
611 {
612   printf("OP_PAUSE_PLAYING: %d\n", buffer[0]);
613   Error(ERR_NETWORK_CLIENT, "client %d pauses game", buffer[0]);
614
615   tape.pausing = TRUE;
616   DrawVideoDisplay(VIDEO_STATE_PAUSE_ON,0);
617 }
618
619 static void Handle_OP_CONTINUE_PLAYING()
620 {
621   printf("OP_CONTINUE_PLAYING: %d\n", buffer[0]);
622   Error(ERR_NETWORK_CLIENT, "client %d continues game", buffer[0]);
623
624   tape.pausing = FALSE;
625   DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
626 }
627
628 static void Handle_OP_STOP_PLAYING()
629 {
630   printf("OP_STOP_PLAYING: %d [%d]\n", buffer[0], buffer[2]);
631   Error(ERR_NETWORK_CLIENT, "client %d stops game [%d]", buffer[0], buffer[2]);
632
633   if (game_status == GAME_MODE_PLAYING)
634   {
635     int client_nr = buffer[0];
636     int index_nr = client_nr - 1;
637     struct PlayerInfo *client_player = &stored_player[index_nr];
638     boolean stopped_by_remote_player = (!client_player->connected_locally);
639     char *message = (buffer[2] == NETWORK_STOP_BY_PLAYER ?
640                      "Network game stopped by player!" :
641                      buffer[2] == NETWORK_STOP_BY_ERROR ?
642                      "Network game stopped due to internal error!" :
643                      "Network game stopped!");
644
645     if (buffer[2] != NETWORK_STOP_BY_PLAYER || stopped_by_remote_player)
646       Request(message, REQ_CONFIRM | REQ_STAY_CLOSED);
647   }
648
649   SetGameStatus(GAME_MODE_MAIN);
650
651   DrawMainMenu();
652 }
653
654 static void Handle_OP_MOVE_PLAYER(unsigned int len)
655 {
656   int server_frame_counter;
657   int i;
658
659   if (!network_playing)
660     return;
661
662   server_frame_counter =
663     (buffer[2] << 24) | (buffer[3] << 16) | (buffer[4] << 8) | (buffer[5]);
664
665   if (server_frame_counter != FrameCounter)
666   {
667     Error(ERR_INFO, "client and servers frame counters out of sync");
668     Error(ERR_INFO, "frame counter of client is %d", FrameCounter);
669     Error(ERR_INFO, "frame counter of server is %d", server_frame_counter);
670     Error(ERR_INFO, "this should not happen -- please debug");
671
672     stop_network_game = TRUE;
673
674     return;
675   }
676
677   /* copy valid player actions */
678   for (i = 0; i < MAX_PLAYERS; i++)
679     stored_player[i].effective_action =
680       (i < len - 6 ? buffer[6 + i] : 0);
681
682   network_player_action_received = TRUE;
683 }
684
685 static void HandleNetworkingMessages()
686 {
687   unsigned int message_length;
688
689   stop_network_game = FALSE;
690
691   while (nread >= 4 && nread >= 4 + readbuffer[3])
692   {
693     message_length = readbuffer[3];
694     if (readbuffer[0] || readbuffer[1] || readbuffer[2])
695       Error(ERR_EXIT, "wrong network server line length");
696
697     memcpy(buffer, &readbuffer[4], message_length);
698     nread -= 4 + message_length;
699     memmove(readbuffer, readbuffer + 4 + message_length, nread);
700
701     switch (buffer[1])
702     {
703       case OP_BAD_PROTOCOL_VERSION:
704         Handle_OP_BAD_PROTOCOL_VERSION();
705         break;
706
707       case OP_YOUR_NUMBER:
708         Handle_OP_YOUR_NUMBER();
709         break;
710
711       case OP_NUMBER_WANTED:
712         Handle_OP_NUMBER_WANTED();
713         break;
714
715       case OP_PLAYER_NAME:
716         Handle_OP_PLAYER_NAME(message_length);
717         break;
718
719       case OP_PLAYER_CONNECTED:
720         Handle_OP_PLAYER_CONNECTED();
721         break;
722       
723       case OP_PLAYER_DISCONNECTED:
724         Handle_OP_PLAYER_DISCONNECTED();
725         break;
726
727       case OP_START_PLAYING:
728         Handle_OP_START_PLAYING();
729         break;
730
731       case OP_PAUSE_PLAYING:
732         Handle_OP_PAUSE_PLAYING();
733         break;
734
735       case OP_CONTINUE_PLAYING:
736         Handle_OP_CONTINUE_PLAYING();
737         break;
738
739       case OP_STOP_PLAYING:
740         Handle_OP_STOP_PLAYING();
741         break;
742
743       case OP_MOVE_PLAYER:
744         Handle_OP_MOVE_PLAYER(message_length);
745         break;
746
747       case OP_BROADCAST_MESSAGE:
748         printf("OP_BROADCAST_MESSAGE: %d\n", buffer[0]);
749         Error(ERR_NETWORK_CLIENT, "client %d sends message", buffer[0]);
750         break;
751     }
752   }
753
754   fflush(stdout);
755
756   /* in case of internal error, stop network game */
757   if (stop_network_game)
758     SendToServer_StopPlaying(NETWORK_STOP_BY_ERROR);
759 }
760
761 static char *HandleNetworkingPackets()
762 {
763   while (1)
764   {
765     /* ---------- check network server for activity ---------- */
766
767     int num_active_sockets = SDLNet_CheckSockets(rfds, 1);
768
769     if (num_active_sockets < 0)
770       return "Error checking network sockets!";
771
772     if (num_active_sockets == 0)
773       break;    // no active sockets, stop here
774
775     /* ---------- read packets from network server ---------- */
776
777     int num_bytes = SDLNet_TCP_Recv(sfd, readbuffer + nread, 1);
778
779     if (num_bytes < 0)
780       return "Error reading from network server!";
781
782     if (num_bytes == 0)
783       return "Connection to network server lost!";
784
785     nread += num_bytes;
786
787     HandleNetworkingMessages();
788   }
789
790   return NULL;
791 }
792
793 static void HandleNetworkingDisconnect()
794 {
795   int i;
796
797   SDLNet_TCP_DelSocket(rfds, sfd);
798   SDLNet_TCP_Close(sfd);
799
800   network_playing = FALSE;
801
802   network.enabled = FALSE;
803   network.connected = FALSE;
804
805   setup.network_mode = FALSE;
806
807   for (i = 0; i < MAX_PLAYERS; i++)
808     stored_player[i].connected_network = FALSE;
809 }
810
811 void HandleNetworking()
812 {
813   char *error_message = HandleNetworkingPackets();
814
815   if (error_message != NULL)
816   {
817     HandleNetworkingDisconnect();
818
819     if (game_status == GAME_MODE_PLAYING)
820     {
821       Request(error_message, REQ_CONFIRM | REQ_STAY_CLOSED);
822
823       SetGameStatus(GAME_MODE_MAIN);
824
825       DrawMainMenu();
826     }
827     else
828     {
829       Request(error_message, REQ_CONFIRM);
830     }
831   }
832 }
833
834 void DisconnectFromNetworkServer()
835 {
836   DrawNetworkText_Title("Terminating Network");
837   DrawNetworkText("Disconnecting from network server ...");
838
839   HandleNetworkingDisconnect();
840
841   DrawNetworkText_Success("Successfully disconnected!");
842
843   /* short time to recognize result of network initialization */
844   Delay(1000);
845 }