0b3c6a8f0b6c27be5385663fbb6311e4d3e5cd07
[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   stored_player[new_index_nr].connected_network = TRUE;
467 }
468
469 static void Handle_OP_NUMBER_WANTED()
470 {
471   int client_nr_wanted = buffer[2];
472   int old_client_nr = buffer[0];
473   int new_client_nr = buffer[3];
474   int old_index_nr = old_client_nr - 1;
475   int new_index_nr = new_client_nr - 1;
476   int index_nr_wanted = client_nr_wanted - 1;
477   struct PlayerInfo *old_player = &stored_player[old_index_nr];
478   struct PlayerInfo *new_player = &stored_player[new_index_nr];
479
480   printf("OP_NUMBER_WANTED: %d\n", buffer[0]);
481
482   if (new_client_nr == client_nr_wanted)        /* switching succeeded */
483   {
484     struct NetworkClientPlayerInfo *player;
485
486     if (old_client_nr != client_nr_wanted)      /* client's nr has changed */
487       Error(ERR_NETWORK_CLIENT, "client %d switches to # %d",
488             old_client_nr, new_client_nr);
489     else if (old_client_nr == first_player.nr)  /* local player keeps his nr */
490       Error(ERR_NETWORK_CLIENT, "keeping client # %d", new_client_nr);
491
492     if (old_client_nr != new_client_nr)
493     {
494       /* set relevant player settings and change to new player */
495
496       old_player->connected_network = FALSE;
497       new_player->connected_network = TRUE;
498     }
499
500     player = getNetworkPlayer(old_client_nr);
501     player->nr = new_client_nr;
502
503     if (old_player == local_player)             /* local player switched */
504     {
505       local_player = new_player;
506
507       old_player->connected_locally = FALSE;
508       new_player->connected_locally = TRUE;
509     }
510   }
511   else if (old_client_nr == first_player.nr)    /* failed -- local player? */
512   {
513     char request[100];
514
515     sprintf(request, "Sorry! Player %d already exists! You are player %d!",
516             index_nr_wanted + 1, new_index_nr + 1);
517
518     Request(request, REQ_CONFIRM);
519
520     Error(ERR_NETWORK_CLIENT, "cannot switch -- you keep client # %d",
521           new_client_nr);
522   }
523
524   if (game_status == GAME_MODE_MAIN)
525     DrawNetworkPlayers();
526 }
527
528 static void Handle_OP_PLAYER_NAME(unsigned int len)
529 {
530   struct NetworkClientPlayerInfo *player;
531   int player_nr = (int)buffer[0];
532
533   printf("OP_PLAYER_NAME: %d\n", player_nr);
534   player = getNetworkPlayer(player_nr);
535   buffer[len] = 0;
536   Error(ERR_NETWORK_CLIENT, "client %d calls itself \"%s\"",
537         buffer[0], &buffer[2]);
538   strncpy(player->name, (char *)&buffer[2], MAX_PLAYER_NAME_LEN);
539 }
540
541 static void Handle_OP_PLAYER_CONNECTED()
542 {
543   struct NetworkClientPlayerInfo *player, *last_player = NULL;
544   int new_client_nr = (int)buffer[0];
545   int new_index_nr = new_client_nr - 1;
546
547   printf("OP_PLAYER_CONNECTED: %d\n", new_client_nr);
548   Error(ERR_NETWORK_CLIENT, "new client %d connected", new_client_nr);
549
550   for (player = &first_player; player; player = player->next)
551   {
552     if (player->nr == new_client_nr)
553       Error(ERR_EXIT, "multiplayer server sent duplicate player id");
554
555     last_player = player;
556   }
557
558   last_player->next = player =
559     checked_malloc(sizeof(struct NetworkClientPlayerInfo));
560   player->nr = new_client_nr;
561   player->name[0] = '\0';
562   player->next = NULL;
563
564   stored_player[new_index_nr].connected_network = TRUE;
565 }
566
567 static void Handle_OP_PLAYER_DISCONNECTED()
568 {
569   struct NetworkClientPlayerInfo *player, *player_disconnected;
570   int player_nr = (int)buffer[0];
571   int index_nr = player_nr - 1;
572
573   printf("OP_PLAYER_DISCONNECTED: %d\n", player_nr);
574   player_disconnected = getNetworkPlayer(player_nr);
575   Error(ERR_NETWORK_CLIENT, "client %d (%s) disconnected",
576         player_nr, getNetworkPlayerName(buffer[0]));
577
578   for (player = &first_player; player; player = player->next)
579     if (player->next == player_disconnected)
580       player->next = player_disconnected->next;
581   free(player_disconnected);
582
583   stored_player[index_nr].connected_locally = FALSE;
584   stored_player[index_nr].connected_network = FALSE;
585
586   if (game_status == GAME_MODE_PLAYING)
587   {
588     char message[100];
589
590     sprintf(message, "Player %d left network server! Network game stopped!",
591             player_nr);
592
593     Request(message, REQ_CONFIRM | REQ_STAY_CLOSED);
594
595     SetGameStatus(GAME_MODE_MAIN);
596
597     DrawMainMenu();
598   }
599   else if (game_status == GAME_MODE_MAIN)
600   {
601     DrawNetworkPlayers();
602   }
603 }
604
605 static void Handle_OP_START_PLAYING()
606 {
607   LevelDirTree *new_leveldir;
608   int new_level_nr;
609   unsigned int new_random_seed;
610   char *new_leveldir_identifier;
611
612   new_level_nr = (buffer[2] << 8) + buffer[3];
613   new_random_seed =
614     (buffer[6] << 24) | (buffer[7] << 16) | (buffer[8] << 8) | (buffer[9]);
615   new_leveldir_identifier = (char *)&buffer[10];
616
617   new_leveldir = getTreeInfoFromIdentifier(leveldir_first,
618                                            new_leveldir_identifier);
619   if (new_leveldir == NULL)
620   {
621     Error(ERR_WARN, "no such level identifier: '%s'", new_leveldir_identifier);
622
623     stop_network_game = TRUE;
624
625     return;
626   }
627
628   printf("OP_START_PLAYING: %d\n", buffer[0]);
629   Error(ERR_NETWORK_CLIENT,
630         "client %d starts game [level %d from level identifier '%s']\n",
631         buffer[0], new_level_nr, new_leveldir->identifier);
632
633   leveldir_current = new_leveldir;
634   level_nr = new_level_nr;
635
636   TapeErase();
637   LoadTape(level_nr);
638   LoadLevel(level_nr);
639
640   StartGameActions(FALSE, setup.autorecord, new_random_seed);
641 }
642
643 static void Handle_OP_PAUSE_PLAYING()
644 {
645   printf("OP_PAUSE_PLAYING: %d\n", buffer[0]);
646   Error(ERR_NETWORK_CLIENT, "client %d pauses game", buffer[0]);
647
648   tape.pausing = TRUE;
649   DrawVideoDisplay(VIDEO_STATE_PAUSE_ON,0);
650 }
651
652 static void Handle_OP_CONTINUE_PLAYING()
653 {
654   printf("OP_CONTINUE_PLAYING: %d\n", buffer[0]);
655   Error(ERR_NETWORK_CLIENT, "client %d continues game", buffer[0]);
656
657   tape.pausing = FALSE;
658   DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
659 }
660
661 static void Handle_OP_STOP_PLAYING()
662 {
663   printf("OP_STOP_PLAYING: %d [%d]\n", buffer[0], buffer[2]);
664   Error(ERR_NETWORK_CLIENT, "client %d stops game [%d]", buffer[0], buffer[2]);
665
666   if (game_status == GAME_MODE_PLAYING)
667   {
668     int client_nr = buffer[0];
669     int index_nr = client_nr - 1;
670     struct PlayerInfo *client_player = &stored_player[index_nr];
671     boolean stopped_by_remote_player = (!client_player->connected_locally);
672     char message[100];
673
674     sprintf(message, (buffer[2] == NETWORK_STOP_BY_PLAYER ?
675                       "Network game stopped by player %d!" :
676                       buffer[2] == NETWORK_STOP_BY_ERROR ?
677                       "Network game stopped due to internal error!" :
678                       "Network game stopped!"), client_nr);
679
680     if (buffer[2] != NETWORK_STOP_BY_PLAYER || stopped_by_remote_player)
681       Request(message, REQ_CONFIRM | REQ_STAY_CLOSED);
682
683     SetGameStatus(GAME_MODE_MAIN);
684
685     DrawMainMenu();
686   }
687 }
688
689 static void Handle_OP_MOVE_PLAYER(unsigned int len)
690 {
691   int server_frame_counter;
692   int i;
693
694   if (!network_playing)
695     return;
696
697   server_frame_counter =
698     (buffer[2] << 24) | (buffer[3] << 16) | (buffer[4] << 8) | (buffer[5]);
699
700   if (server_frame_counter != FrameCounter)
701   {
702     Error(ERR_INFO, "client and servers frame counters out of sync");
703     Error(ERR_INFO, "frame counter of client is %d", FrameCounter);
704     Error(ERR_INFO, "frame counter of server is %d", server_frame_counter);
705     Error(ERR_INFO, "this should not happen -- please debug");
706
707     stop_network_game = TRUE;
708
709     return;
710   }
711
712   /* copy valid player actions */
713   for (i = 0; i < MAX_PLAYERS; i++)
714     stored_player[i].effective_action =
715       (i < len - 6 ? buffer[6 + i] : 0);
716
717   network_player_action_received = TRUE;
718 }
719
720 static void HandleNetworkingMessages()
721 {
722   unsigned int message_length;
723
724   stop_network_game = FALSE;
725
726   while (nread >= 4 && nread >= 4 + readbuffer[3])
727   {
728     message_length = readbuffer[3];
729     if (readbuffer[0] || readbuffer[1] || readbuffer[2])
730       Error(ERR_EXIT, "wrong network server line length");
731
732     memcpy(buffer, &readbuffer[4], message_length);
733     nread -= 4 + message_length;
734     memmove(readbuffer, readbuffer + 4 + message_length, nread);
735
736     switch (buffer[1])
737     {
738       case OP_BAD_PROTOCOL_VERSION:
739         Handle_OP_BAD_PROTOCOL_VERSION();
740         break;
741
742       case OP_YOUR_NUMBER:
743         Handle_OP_YOUR_NUMBER();
744         break;
745
746       case OP_NUMBER_WANTED:
747         Handle_OP_NUMBER_WANTED();
748         break;
749
750       case OP_PLAYER_NAME:
751         Handle_OP_PLAYER_NAME(message_length);
752         break;
753
754       case OP_PLAYER_CONNECTED:
755         Handle_OP_PLAYER_CONNECTED();
756         break;
757       
758       case OP_PLAYER_DISCONNECTED:
759         Handle_OP_PLAYER_DISCONNECTED();
760         break;
761
762       case OP_START_PLAYING:
763         Handle_OP_START_PLAYING();
764         break;
765
766       case OP_PAUSE_PLAYING:
767         Handle_OP_PAUSE_PLAYING();
768         break;
769
770       case OP_CONTINUE_PLAYING:
771         Handle_OP_CONTINUE_PLAYING();
772         break;
773
774       case OP_STOP_PLAYING:
775         Handle_OP_STOP_PLAYING();
776         break;
777
778       case OP_MOVE_PLAYER:
779         Handle_OP_MOVE_PLAYER(message_length);
780         break;
781
782       case OP_BROADCAST_MESSAGE:
783         printf("OP_BROADCAST_MESSAGE: %d\n", buffer[0]);
784         Error(ERR_NETWORK_CLIENT, "client %d sends message", buffer[0]);
785         break;
786     }
787   }
788
789   fflush(stdout);
790
791   /* in case of internal error, stop network game */
792   if (stop_network_game)
793     SendToServer_StopPlaying(NETWORK_STOP_BY_ERROR);
794 }
795
796 static char *HandleNetworkingPackets()
797 {
798   while (1)
799   {
800     /* ---------- check network server for activity ---------- */
801
802     int num_active_sockets = SDLNet_CheckSockets(rfds, 1);
803
804     if (num_active_sockets < 0)
805       return "Error checking network sockets!";
806
807     if (num_active_sockets == 0)
808       break;    // no active sockets, stop here
809
810     /* ---------- read packets from network server ---------- */
811
812     int num_bytes = SDLNet_TCP_Recv(sfd, readbuffer + nread, 1);
813
814     if (num_bytes < 0)
815       return "Error reading from network server!";
816
817     if (num_bytes == 0)
818       return "Connection to network server lost!";
819
820     nread += num_bytes;
821
822     HandleNetworkingMessages();
823   }
824
825   return NULL;
826 }
827
828 static void FreeNetworkClientPlayerInfo(struct NetworkClientPlayerInfo *player)
829 {
830   if (player == NULL)
831     return;
832
833   if (player->next)
834     FreeNetworkClientPlayerInfo(player->next);
835
836   checked_free(player);
837 }
838
839 static void HandleNetworkingDisconnect()
840 {
841   int i;
842
843   SDLNet_TCP_DelSocket(rfds, sfd);
844   SDLNet_TCP_Close(sfd);
845
846   network_playing = FALSE;
847
848   network.enabled = FALSE;
849   network.connected = FALSE;
850
851   setup.network_mode = FALSE;
852
853   for (i = 0; i < MAX_PLAYERS; i++)
854     stored_player[i].connected_network = FALSE;
855
856   FreeNetworkClientPlayerInfo(first_player.next);
857
858   first_player.nr = 0;
859   first_player.next = NULL;
860 }
861
862 void HandleNetworking()
863 {
864   char *error_message = HandleNetworkingPackets();
865
866   if (error_message != NULL)
867   {
868     HandleNetworkingDisconnect();
869
870     if (game_status == GAME_MODE_PLAYING)
871     {
872       Request(error_message, REQ_CONFIRM | REQ_STAY_CLOSED);
873
874       SetGameStatus(GAME_MODE_MAIN);
875
876       DrawMainMenu();
877     }
878     else
879     {
880       Request(error_message, REQ_CONFIRM);
881
882       if (game_status == GAME_MODE_MAIN)
883         ClearNetworkPlayers();
884     }
885   }
886 }
887
888 void DisconnectFromNetworkServer()
889 {
890   DrawNetworkText_Title("Terminating Network");
891   DrawNetworkText("Disconnecting from network server ...");
892
893   HandleNetworkingDisconnect();
894
895   DrawNetworkText_Success("Successfully disconnected!");
896
897   /* short time to recognize result of network initialization */
898   Delay_WithScreenUpdates(1000);
899 }