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