added UDP broadcast to auto-detect network server in local network
[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 "libgame/platform.h"
13
14 #if defined(NETWORK_AVALIABLE)
15
16 #include <signal.h>
17 #include <sys/time.h>
18
19 #include "main.h"
20
21 #include "libgame/libgame.h"
22
23 #include "network.h"
24 #include "netserv.h"
25 #include "game.h"
26 #include "tape.h"
27 #include "files.h"
28 #include "tools.h"
29 #include "screens.h"
30
31 struct NetworkClientPlayerInfo
32 {
33   byte nr;
34   char name[MAX_PLAYER_NAME_LEN + 1];
35   struct NetworkClientPlayerInfo *next;
36 };
37
38 static struct NetworkClientPlayerInfo first_player =
39 {
40   0,
41   EMPTY_PLAYER_NAME,
42   NULL
43 };
44
45 /* server stuff */
46
47 static TCPsocket sfd;           /* TCP server socket */
48 static UDPsocket udp;           /* UDP server socket */
49 static SDLNet_SocketSet rfds;   /* socket set */
50
51 static byte realbuffer[512];
52 static byte readbuffer[MAX_BUFFER_SIZE], writbuffer[MAX_BUFFER_SIZE];
53 static byte *buffer = realbuffer + 4;
54 static int nread = 0, nwrite = 0;
55 static boolean stop_network_game = FALSE;
56
57 static void SendBufferToServer(int size)
58 {
59   if (!options.network)
60     return;
61
62   realbuffer[0] = realbuffer[1] = realbuffer[2] = 0;
63   realbuffer[3] = (byte)size;
64   buffer[0] = 0;
65
66   if (nwrite + 4 + size >= MAX_BUFFER_SIZE)
67     Error(ERR_EXIT, "internal error: network send buffer overflow");
68
69   memcpy(writbuffer + nwrite, realbuffer, 4 + size);
70   nwrite += 4 + size;
71
72   /* directly send the buffer to the network server */
73   SDLNet_TCP_Send(sfd, writbuffer, nwrite);
74   nwrite = 0;
75 }
76
77 struct NetworkClientPlayerInfo *getNetworkPlayer(int player_nr)
78 {
79   struct NetworkClientPlayerInfo *player = NULL;
80
81   for (player = &first_player; player; player = player->next)
82     if (player->nr == player_nr)
83       break;
84
85   if (player == NULL)   /* should not happen */
86     Error(ERR_EXIT, "protocol error: reference to non-existing player %d",
87           player_nr);
88
89   return player;
90 }
91
92 char *getNetworkPlayerName(int player_nr)
93 {
94   struct NetworkClientPlayerInfo *player;
95
96   if (player_nr == 0)
97     return("the network game server");
98   else if (player_nr == first_player.nr)
99     return("you");
100   else
101     for (player = &first_player; player; player = player->next)
102       if (player->nr == player_nr && strlen(player->name) > 0)
103         return(player->name);
104
105   return(EMPTY_PLAYER_NAME);
106 }
107
108 static void StartNetworkServer(int port)
109 {
110   static int p;
111
112   p = port;
113 #if defined(TARGET_SDL2)
114   server_thread = SDL_CreateThread(NetworkServerThread,
115                                    "NetworkServerThread", &p);
116 #else
117   server_thread = SDL_CreateThread(NetworkServerThread, &p);
118 #endif
119   network_server = TRUE;
120 }
121
122 boolean ConnectToServer(char *hostname, int port)
123 {
124   IPaddress ip;
125   int server_host = 0;
126   int i;
127
128   if (port == 0)
129     port = DEFAULT_SERVER_PORT;
130
131   if (hostname == NULL)
132   {
133     // if no hostname given, try to auto-detect network server in local network
134     // by doing a UDP broadcast on the network server port and wait for answer
135
136     SDLNet_SocketSet udp_socket_set = SDLNet_AllocSocketSet(1);
137     if (!udp_socket_set)
138       Error(ERR_EXIT, "SDLNet_AllocSocketSet() failed: %s"), SDLNet_GetError();
139
140     udp = SDLNet_UDP_Open(0);
141     if(!udp)
142       Error(ERR_EXIT, "SDLNet_UDP_Open() failed: %s", SDLNet_GetError());
143
144     if (SDLNet_UDP_AddSocket(udp_socket_set, udp) == -1)
145       Error(ERR_EXIT_NETWORK_SERVER, "SDLNet_TCP_AddSocket() failed: %s"),
146         SDLNet_GetError();
147
148     char *data_ptr = "network server UDB broadcast";
149     int data_len = strlen(data_ptr) + 1;
150     IPaddress ip_address;
151
152     SDLNet_Write32(0xffffffff, &ip_address.host);       /* 255.255.255.255 */
153     SDLNet_Write16(port,       &ip_address.port);
154
155     UDPpacket packet =
156     {
157       -1,
158       (Uint8 *)data_ptr,
159       data_len,
160       data_len,
161       0,
162       ip_address
163     };
164
165     SDLNet_UDP_Send(udp, -1, &packet);
166
167     Error(ERR_DEBUG, "doing UDP broadcast for local network server ...");
168
169     if (SDLNet_CheckSockets(udp_socket_set, 1000) == 1)
170     {
171       int num_packets = SDLNet_UDP_Recv(udp, &packet);
172
173       if (num_packets == 1)
174       {
175         Error(ERR_DEBUG, "network server found");
176
177         server_host = SDLNet_Read32(&packet.address.host);
178       }
179       else
180       {
181         Error(ERR_DEBUG, "no answer from network server");
182       }
183     }
184     else
185     {
186       Error(ERR_DEBUG, "no network server found");
187     }
188   }
189
190   rfds = SDLNet_AllocSocketSet(1);
191
192   if (hostname)
193   {
194     SDLNet_ResolveHost(&ip, hostname, port);
195
196     if (ip.host == INADDR_NONE)
197       Error(ERR_EXIT, "cannot locate host '%s'", hostname);
198     else
199       server_host = SDLNet_Read32(&ip.host);
200   }
201   else
202   {
203     // if no hostname was given and no network server was auto-detected in the
204     // local network, try to connect to a network server at the local host
205     if (server_host == 0)
206       server_host = 0x7f000001;                 /* 127.0.0.1 */
207
208     SDLNet_Write32(server_host, &ip.host);
209     SDLNet_Write16(port,        &ip.port);
210   }
211
212   Error(ERR_DEBUG, "trying to connect to network server at %d.%d.%d.%d ...",
213         (server_host >> 24) & 0xff,
214         (server_host >> 16) & 0xff,
215         (server_host >>  8) & 0xff,
216         (server_host >>  0) & 0xff);
217
218   sfd = SDLNet_TCP_Open(&ip);
219
220   if (sfd)
221   {
222     SDLNet_TCP_AddSocket(rfds, sfd);
223     return TRUE;
224   }
225   else
226   {
227     printf("SDLNet_TCP_Open(): %s\n", SDLNet_GetError());
228   }
229
230   if (hostname)                 /* connect to specified server failed */
231     return FALSE;
232
233   printf("No rocksndiamonds server on localhost -- starting up one ...\n");
234   StartNetworkServer(port);
235
236   /* wait for server to start up and try connecting several times */
237   for (i = 0; i < 6; i++)
238   {
239     Delay(500);                 /* wait 500 ms == 0.5 seconds */
240
241     if ((sfd = SDLNet_TCP_Open(&ip)))           /* connected */
242     {
243       SDLNet_TCP_AddSocket(rfds, sfd);
244       return TRUE;
245     }
246   }
247
248   /* when reaching this point, connect to newly started server has failed */
249   return FALSE;
250 }
251
252 void SendToServer_PlayerName(char *player_name)
253 {
254   int len_player_name = strlen(player_name);
255
256   buffer[1] = OP_PLAYER_NAME;
257   memcpy(&buffer[2], player_name, len_player_name);
258   SendBufferToServer(2 + len_player_name);
259   Error(ERR_NETWORK_CLIENT, "you set your player name to \"%s\"", player_name);
260 }
261
262 void SendToServer_ProtocolVersion()
263 {
264   buffer[1] = OP_PROTOCOL_VERSION;
265   buffer[2] = PROTOCOL_VERSION_1;
266   buffer[3] = PROTOCOL_VERSION_2;
267   buffer[4] = PROTOCOL_VERSION_3;
268
269   SendBufferToServer(5);
270 }
271
272 void SendToServer_NrWanted(int nr_wanted)
273 {
274   buffer[1] = OP_NUMBER_WANTED;
275   buffer[2] = nr_wanted;
276
277   SendBufferToServer(3);
278 }
279
280 void SendToServer_StartPlaying()
281 {
282   unsigned int new_random_seed = InitRND(level.random_seed);
283
284   int dummy = 0;                /* !!! HAS NO MEANING ANYMORE !!! */
285                                 /* the name of the level must be enough */
286
287   buffer[1] = OP_START_PLAYING;
288   buffer[2] = (byte)(level_nr >> 8);
289   buffer[3] = (byte)(level_nr & 0xff);
290   buffer[4] = (byte)(dummy >> 8);
291   buffer[5] = (byte)(dummy & 0xff);
292
293   buffer[6] = (unsigned char)((new_random_seed >> 24) & 0xff);
294   buffer[7] = (unsigned char)((new_random_seed >> 16) & 0xff);
295   buffer[8] = (unsigned char)((new_random_seed >>  8) & 0xff);
296   buffer[9] = (unsigned char)((new_random_seed >>  0) & 0xff);
297
298   strcpy((char *)&buffer[10], leveldir_current->identifier);
299
300   SendBufferToServer(10 + strlen(leveldir_current->identifier) + 1);
301 }
302
303 void SendToServer_PausePlaying()
304 {
305   buffer[1] = OP_PAUSE_PLAYING;
306
307   SendBufferToServer(2);
308 }
309
310 void SendToServer_ContinuePlaying()
311 {
312   buffer[1] = OP_CONTINUE_PLAYING;
313
314   SendBufferToServer(2);
315 }
316
317 void SendToServer_StopPlaying(int cause_for_stopping)
318 {
319   buffer[1] = OP_STOP_PLAYING;
320   buffer[2] = cause_for_stopping;
321
322   SendBufferToServer(3);
323 }
324
325 void SendToServer_MovePlayer(byte player_action)
326 {
327   buffer[1] = OP_MOVE_PLAYER;
328   buffer[2] = player_action;
329
330   SendBufferToServer(3);
331 }
332
333 static void Handle_OP_BAD_PROTOCOL_VERSION()
334 {
335   Error(ERR_WARN, "protocol version mismatch");
336   Error(ERR_EXIT, "server expects %d.%d.x instead of %d.%d.%d",
337         buffer[2], buffer[3],
338         PROTOCOL_VERSION_1, PROTOCOL_VERSION_2, PROTOCOL_VERSION_3);
339 }
340
341 static void Handle_OP_YOUR_NUMBER()
342 {
343   int new_client_nr = buffer[2];
344   int new_index_nr = new_client_nr - 1;
345   struct PlayerInfo *old_local_player = local_player;
346   struct PlayerInfo *new_local_player = &stored_player[new_index_nr];
347
348   printf("OP_YOUR_NUMBER: %d\n", buffer[0]);
349   first_player.nr = new_client_nr;
350
351   if (old_local_player != new_local_player)
352   {
353     /* set relevant player settings and change to new player */
354
355     local_player = new_local_player;
356
357     old_local_player->connected_locally = FALSE;
358     new_local_player->connected_locally = TRUE;
359
360     old_local_player->connected_network = FALSE;
361     new_local_player->connected_network = TRUE;
362   }
363
364   if (first_player.nr > MAX_PLAYERS)
365     Error(ERR_EXIT, "sorry, more than %d players not allowed", MAX_PLAYERS);
366
367   Error(ERR_NETWORK_CLIENT, "you get client # %d", new_client_nr);
368 }
369
370 static void Handle_OP_NUMBER_WANTED()
371 {
372   int client_nr_wanted = buffer[2];
373   int old_client_nr = buffer[0];
374   int new_client_nr = buffer[3];
375   int old_index_nr = old_client_nr - 1;
376   int new_index_nr = new_client_nr - 1;
377   int index_nr_wanted = client_nr_wanted - 1;
378   struct PlayerInfo *old_player = &stored_player[old_index_nr];
379   struct PlayerInfo *new_player = &stored_player[new_index_nr];
380
381   printf("OP_NUMBER_WANTED: %d\n", buffer[0]);
382
383   if (new_client_nr == client_nr_wanted)        /* switching succeeded */
384   {
385     struct NetworkClientPlayerInfo *player;
386
387     if (old_client_nr != client_nr_wanted)      /* client's nr has changed */
388       Error(ERR_NETWORK_CLIENT, "client %d switches to # %d",
389             old_client_nr, new_client_nr);
390     else if (old_client_nr == first_player.nr)  /* local player keeps his nr */
391       Error(ERR_NETWORK_CLIENT, "keeping client # %d", new_client_nr);
392
393     if (old_client_nr != new_client_nr)
394     {
395       /* set relevant player settings and change to new player */
396
397       old_player->connected_network = FALSE;
398       new_player->connected_network = TRUE;
399     }
400
401     player = getNetworkPlayer(old_client_nr);
402     player->nr = new_client_nr;
403
404     if (old_player == local_player)             /* local player switched */
405     {
406       local_player = new_player;
407
408       old_player->connected_locally = FALSE;
409       new_player->connected_locally = TRUE;
410     }
411   }
412   else if (old_client_nr == first_player.nr)    /* failed -- local player? */
413   {
414     char request[100];
415
416     sprintf(request, "Sorry! Player %d already exists! You are player %d!",
417             index_nr_wanted + 1, new_index_nr + 1);
418
419     Request(request, REQ_CONFIRM);
420
421     Error(ERR_NETWORK_CLIENT, "cannot switch -- you keep client # %d",
422           new_client_nr);
423   }
424 }
425
426 static void Handle_OP_PLAYER_NAME(unsigned int len)
427 {
428   struct NetworkClientPlayerInfo *player;
429   int player_nr = (int)buffer[0];
430
431   printf("OP_PLAYER_NAME: %d\n", player_nr);
432   player = getNetworkPlayer(player_nr);
433   buffer[len] = 0;
434   Error(ERR_NETWORK_CLIENT, "client %d calls itself \"%s\"",
435         buffer[0], &buffer[2]);
436   strncpy(player->name, (char *)&buffer[2], MAX_PLAYER_NAME_LEN);
437 }
438
439 static void Handle_OP_PLAYER_CONNECTED()
440 {
441   struct NetworkClientPlayerInfo *player, *last_player = NULL;
442   int new_client_nr = (int)buffer[0];
443   int new_index_nr = new_client_nr - 1;
444
445   printf("OP_PLAYER_CONNECTED: %d\n", new_client_nr);
446   Error(ERR_NETWORK_CLIENT, "new client %d connected", new_client_nr);
447
448   for (player = &first_player; player; player = player->next)
449   {
450     if (player->nr == new_client_nr)
451       Error(ERR_EXIT, "multiplayer server sent duplicate player id");
452
453     last_player = player;
454   }
455
456   last_player->next = player =
457     checked_malloc(sizeof(struct NetworkClientPlayerInfo));
458   player->nr = new_client_nr;
459   player->name[0] = '\0';
460   player->next = NULL;
461
462   stored_player[new_index_nr].connected_network = TRUE;
463 }
464
465 static void Handle_OP_PLAYER_DISCONNECTED()
466 {
467   struct NetworkClientPlayerInfo *player, *player_disconnected;
468   int player_nr = (int)buffer[0];
469   int index_nr = player_nr - 1;
470
471   printf("OP_PLAYER_DISCONNECTED: %d\n", player_nr);
472   player_disconnected = getNetworkPlayer(player_nr);
473   Error(ERR_NETWORK_CLIENT, "client %d (%s) disconnected",
474         player_nr, getNetworkPlayerName(buffer[0]));
475
476   for (player = &first_player; player; player = player->next)
477     if (player->next == player_disconnected)
478       player->next = player_disconnected->next;
479   free(player_disconnected);
480
481   stored_player[index_nr].connected_locally = FALSE;
482   stored_player[index_nr].connected_network = FALSE;
483 }
484
485 static void Handle_OP_START_PLAYING()
486 {
487   LevelDirTree *new_leveldir;
488   int new_level_nr;
489   unsigned int new_random_seed;
490   char *new_leveldir_identifier;
491
492   new_level_nr = (buffer[2] << 8) + buffer[3];
493   new_random_seed =
494     (buffer[6] << 24) | (buffer[7] << 16) | (buffer[8] << 8) | (buffer[9]);
495   new_leveldir_identifier = (char *)&buffer[10];
496
497   new_leveldir = getTreeInfoFromIdentifier(leveldir_first,
498                                            new_leveldir_identifier);
499   if (new_leveldir == NULL)
500   {
501     Error(ERR_WARN, "no such level identifier: '%s'", new_leveldir_identifier);
502
503     new_leveldir = leveldir_first;
504     Error(ERR_WARN, "using default level set: '%s'", new_leveldir->identifier);
505   }
506
507   printf("OP_START_PLAYING: %d\n", buffer[0]);
508   Error(ERR_NETWORK_CLIENT,
509         "client %d starts game [level %d from level identifier '%s']\n",
510         buffer[0], new_level_nr, new_leveldir->identifier);
511
512   leveldir_current = new_leveldir;
513   level_nr = new_level_nr;
514
515   TapeErase();
516   LoadTape(level_nr);
517   LoadLevel(level_nr);
518
519   StartGameActions(FALSE, setup.autorecord, new_random_seed);
520 }
521
522 static void Handle_OP_PAUSE_PLAYING()
523 {
524   printf("OP_PAUSE_PLAYING: %d\n", buffer[0]);
525   Error(ERR_NETWORK_CLIENT, "client %d pauses game", buffer[0]);
526
527   tape.pausing = TRUE;
528   DrawVideoDisplay(VIDEO_STATE_PAUSE_ON,0);
529 }
530
531 static void Handle_OP_CONTINUE_PLAYING()
532 {
533   printf("OP_CONTINUE_PLAYING: %d\n", buffer[0]);
534   Error(ERR_NETWORK_CLIENT, "client %d continues game", buffer[0]);
535
536   tape.pausing = FALSE;
537   DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
538 }
539
540 static void Handle_OP_STOP_PLAYING()
541 {
542   printf("OP_STOP_PLAYING: %d [%d]\n", buffer[0], buffer[2]);
543   Error(ERR_NETWORK_CLIENT, "client %d stops game [%d]", buffer[0], buffer[2]);
544
545   if (game_status == GAME_MODE_PLAYING)
546   {
547     if (buffer[2] == NETWORK_STOP_BY_PLAYER)
548       Request("Network game stopped by player!", REQ_CONFIRM);
549     else if (buffer[2] == NETWORK_STOP_BY_ERROR)
550       Request("Network game stopped due to internal error!", REQ_CONFIRM);
551     else
552       Request("Network game stopped!", REQ_CONFIRM);
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 /* TODO */
668
669 void HandleNetworking()
670 {
671   int r = 0;
672
673   do
674   {
675     if ((r = SDLNet_CheckSockets(rfds, 1)) < 0)
676       Error(ERR_EXIT, "HandleNetworking(): SDLNet_CheckSockets() failed");
677
678     if (r > 0)
679     {
680       r = SDLNet_TCP_Recv(sfd, readbuffer + nread, 1);
681
682       if (r < 0)
683         Error(ERR_EXIT, "error reading from network server");
684
685       if (r == 0)
686         Error(ERR_EXIT, "connection to network server lost");
687
688       nread += r;
689
690       HandleNetworkingMessages();
691     }
692   }
693   while (r > 0);
694 }
695
696 #endif /* NETWORK_AVALIABLE */