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