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