1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU Library General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16 
17 #include "netplay.h"
18 
19 extern int gtk_stop_asked;
20 
21 #define DEBUG
22 
23 const int PACKET_IDENTIFIER = 0;
24 
25 const int PACKET_IDENTIFICATION_ID = 0x55;
26 const int PACKET_IDENTIFICATION_ACKNOWLEDGE_ID = 0x96;
27 const int PACKET_STATUS_ID = 0xC2;
28 const int PACKET_DIGEST_ID = 0x3B;
29 const int PACKET_INTERNET_DIGEST_ID = 0x1F;
30 
31 const int PACKET_IDENTIFICATION_NUMBER_REQUESTED_SLOTS = 1;
32 const int PACKET_IDENTIFICATION_INPUT_DEVICE_INDEX = 2;
33 const int PACKET_IDENTIFICATION_INPUT_DEVICE_NUMBER = 5;
34 const int PACKET_IDENTIFICATION_CHECKSUM = 7;
35 const int PACKET_IDENTIFICATION_LENGTH = 8;
36 
37 const int PACKET_IDENTIFICATION_ACKNOWLEDGE_NUMBER_ALLOCATED_SLOTS = 1;
38 const int PACKET_IDENTIFICATION_ACKNOWLEDGE_CHECKSUM = 2;
39 const int PACKET_IDENTIFICATION_ACKNOWLEDGE_LENGTH = 3;
40 
41 const int PACKET_STATUS_FRAME_NUMBER = 1;
42 const int PACKET_STATUS_INPUT_DEVICE_INDEX = 5;
43 const int PACKET_STATUS_INPUT_DEVICE_NUMBER = 5;
44 const int PACKET_STATUS_CHECKSUM = 10;
45 const int PACKET_STATUS_LENGTH = 11;
46 
47 const int PACKET_INTERNET_DIGEST_FRAME_NUMBER = 1;
48 const int PACKET_INTERNET_DIGEST_NUMBER_DIGEST = 5;
49 const int PACKET_INTERNET_DIGEST_DIGEST_INDEX = 6;
50 const int PACKET_INTERNET_DIGEST_DIGEST_NUMBER = 5;
51 const int PACKET_INTERNET_DIGEST_BASE_LENGTH = 7;
52 const int PACKET_INTERNET_DIGEST_INCREMENT_LENGTH = 5;
53 
54 const int MAX_NUMBER_PLAYER = 5;
55 
56 const int MIN_NUMBER_PLAYER = 1;
57 
58 const int SERVER_SOCKET_TIMEOUT = 1000;	/* In millisecond */
59 const int CLIENT_SOCKET_TIMEOUT = 10;	/* In millisecond */
60 const int CLIENT_SOCKET_INTERNET_TIMEOUT = 20;	/* In millisecond */
61 
62 const int CLIENT_PACKET_SIZE = 11;
63 const int SERVER_PACKET_SIZE = 7 + 5 * 5;
64 
65 const int DEFAULT_SERVER_PORT = 25679;
66 
67 const int FRAME_PER_SECOND = 60;
68 const int DIGEST_SIZE = 5;
69 const int BUFFER_SIZE_SECONDS = 60 * 2;	/* two minuts */
70 const int BUFFER_SIZE_BYTES = 60 * 2 * 60 * 5;	/* size in seconds * frame per seconds * digest size */
71 const int AUTO_OUTDATE = 60 * 2 * 60;	/* size in seconds * frame per seconds */
72 
73 const double DOUBLE_FRAME_DURATION = 1.0 / 60.0;	/* In second */
74 const int SDL_TICKS_PER_SECOND = 1000;
75 
76 /*!
77  * In the lan protocol implementation, this array holds the input for the previous frame
78  */
79 static UChar previous_input_value[5];
80 
81 /*!
82  * In the internet protocol implementation, this pointer holds the history */
83 static UChar *history_digest = NULL;
84 
85 //! Send packet for the previous frame
86 void send_previous_digest_packet (global_status_type * global_status,
87 				  global_option_type * global_option,
88 				  IPaddress address);
89 
90 /*!
91  * Initializes the network for netplay. SDL has to be initialized first.
92  */
93 int
init_network()94 init_network ()
95 {
96 
97   if (SDLNet_Init () == -1)
98     {
99       printf ("SDLNet_Init: Error\n");
100       return 2;
101     }
102 
103   return 0;
104 }
105 
106 /*!
107  * Release the network for netplay. It doesn't change the state of SDL
108  */
109 void
shutdown_network()110 shutdown_network ()
111 {
112   SDLNet_Quit ();
113 }
114 
115 /*!
116  * Compute the rotation of the value (equivalent of ROL in ia32 asm)
117  * \param value the value to rotate
118  * \return the rotated value
119  */
120 UChar
rotate_left(UChar value)121 rotate_left (UChar value)
122 {
123   return (UChar) ((value << 1) + ((value & 0x80) ? 1 : 0));
124 }
125 
126 /*!
127  * Compute the check sum value according to the algorithm found in the header file
128  * \param data the array on which perform the computation
129  * \param index_min first index in the array to take into account
130  * \param index_max last index (EXCLUSIVE) in the array to take into account
131  */
132 unsigned char
compute_checksum(unsigned char * data,int index_min,int index_max)133 compute_checksum (unsigned char *data, int index_min, int index_max)
134 {
135   UChar checksum;
136   int index;
137 
138   checksum = 0;
139 
140   for (index = index_min; index < index_max; index++)
141     {
142       checksum ^= data[index];
143       checksum = rotate_left (checksum);
144     }
145 
146   return checksum;
147 }
148 
149 /*!
150  * Side effect free minimum computation
151  */
152 static int
min(int lhs,int rhs)153 min (int lhs, int rhs)
154 {
155   return (lhs < rhs) ? lhs : rhs;
156 }
157 
158 /*!
159  * Returns the number of free (unidentified and under the limit requested) slots
160  */
161 int
count_remaining_slot(global_status_type * global_status,global_option_type * global_option)162 count_remaining_slot (global_status_type * global_status,
163 		      global_option_type * global_option)
164 {
165   return global_option->number_player -
166     global_status->number_identified_players;
167 }
168 
169 /*!
170  * Check if two SDLNet addresses are the same
171  * \return non null value is addresses match, else 0
172  */
173 int
equals_address(IPaddress lhs,IPaddress rhs)174 equals_address (IPaddress lhs, IPaddress rhs)
175 {
176   return (lhs.host == rhs.host) && (lhs.port == rhs.port);
177 }
178 
179 /*!
180  * Build and send a packet to acknowledge an identification. We use the digest
181  * packet structure to prepare it.
182  */
183 void
send_identification_acknowledge_packet(global_status_type * global_status,int number_allocated_slots)184 send_identification_acknowledge_packet (global_status_type * global_status,
185 					int number_allocated_slots)
186 {
187   int number_destination;
188 
189   /* Set the identifier */
190   global_status->digest_packet->data[PACKET_IDENTIFIER] =
191     PACKET_IDENTIFICATION_ACKNOWLEDGE_ID;
192 
193   /* Set the number of allocated slots */
194   global_status->digest_packet->
195     data[PACKET_IDENTIFICATION_ACKNOWLEDGE_NUMBER_ALLOCATED_SLOTS] =
196     number_allocated_slots;
197 
198   /* Compute the checksum */
199   global_status->digest_packet->
200     data[PACKET_IDENTIFICATION_ACKNOWLEDGE_CHECKSUM] =
201     compute_checksum (global_status->digest_packet->data, PACKET_IDENTIFIER,
202 		      PACKET_IDENTIFICATION_ACKNOWLEDGE_CHECKSUM);
203 
204 
205   global_status->digest_packet->len =
206     PACKET_IDENTIFICATION_ACKNOWLEDGE_LENGTH;
207 
208   /* Back to sender */
209   global_status->digest_packet->address =
210     global_status->current_packet->address;
211 
212   number_destination = SDLNet_UDP_Send (global_status->server_socket,
213 					-1, global_status->digest_packet);
214 
215   if (number_destination != 1)
216     {
217       fprintf (stderr,
218 	       "Couldn't send the identification acknowledge packet to client\n");
219     }
220 
221 }
222 
223 /*!
224  * Compare given address with previous address from which slot allocation have been successfully processed
225  * \param global_status the global status for the server
226  * \parem address the address to check
227  * \return -1 is the address was never seen
228  * \return the number of slots we allocated for it back then, else
229  */
230 int
compare_request_to_previous(global_status_type * global_status,IPaddress address)231 compare_request_to_previous (global_status_type * global_status,
232 			     IPaddress address)
233 {
234   int slot_index;
235 
236   for (slot_index = 0; slot_index < global_status->number_allocation_request;
237        slot_index++)
238     {
239       if (equals_address
240 	  (address, global_status->allocation_request[slot_index].address))
241 	{
242 	  /* We found a previous request */
243 	  return global_status->allocation_request[slot_index].
244 	    allocated_slots;
245 	}
246     }
247 
248   return -1;
249 
250 }
251 
252 /*!
253  * Process an identification request from a client, filling the interesting
254  * fields in the global_status structure to find later the client
255  */
256 void
identify_client(global_status_type * global_status,global_option_type * global_option)257 identify_client (global_status_type * global_status,
258 		 global_option_type * global_option)
259 {
260 
261   int requested_slots;
262   int allocated_slots;
263   int slot_index;
264   int already_allocated_slot_by_client;
265 
266   if (global_status->current_packet->len != PACKET_IDENTIFICATION_LENGTH)
267     {
268       /* Discarding invalid packet */
269 #if defined(DEBUG)
270       fprintf (stderr,
271 	       "Invalid packet received when in identification phase (received length: %d, expected length: %d)\n",
272 	       global_status->current_packet->len,
273 	       PACKET_IDENTIFICATION_LENGTH);
274 #endif
275       return;
276     }
277 
278   if (global_status->current_packet->data[PACKET_IDENTIFIER] !=
279       PACKET_IDENTIFICATION_ID)
280     {
281       /* Discarding invalid packet */
282 #if defined(DEBUG)
283       fprintf (stderr,
284 	       "Invalid packet received when in identification phase (received ID: 0x%02x, expected ID: 0x%02x)\n",
285 	       global_status->current_packet->data[PACKET_IDENTIFIER],
286 	       PACKET_IDENTIFICATION_ID);
287 #endif
288       return;
289     }
290 
291   if (global_status->current_packet->data[PACKET_IDENTIFICATION_CHECKSUM] !=
292       compute_checksum (global_status->current_packet->data,
293 			PACKET_IDENTIFIER, PACKET_IDENTIFICATION_CHECKSUM))
294     {
295       /* Discarding invalid packet */
296 #if defined(DEBUG)
297       fprintf (stderr,
298 	       "Packet checksum received when in identification phase (received checksum: 0x%02x, expected checksum: 0x%02x)\n",
299 	       global_status->current_packet->
300 	       data[PACKET_IDENTIFICATION_CHECKSUM],
301 	       compute_checksum (global_status->current_packet->data,
302 				 PACKET_IDENTIFIER,
303 				 PACKET_IDENTIFICATION_CHECKSUM));
304 #endif
305       return;
306     }
307 
308   requested_slots =
309     global_status->current_packet->
310     data[PACKET_IDENTIFICATION_NUMBER_REQUESTED_SLOTS];
311 
312   allocated_slots =
313     min (requested_slots,
314 	 count_remaining_slot (global_status, global_option));
315 
316   /* Check if it's a new request or a re-request (happens when the client didn't get the acknowledgement) */
317   already_allocated_slot_by_client =
318     compare_request_to_previous (global_status,
319 				 global_status->current_packet->address);
320 
321   if (already_allocated_slot_by_client != -1)
322     {
323       /* We already answered this client, we'll reacknowledge */
324       send_identification_acknowledge_packet (global_status,
325 					      already_allocated_slot_by_client);
326 
327       return;
328     }
329 
330   for (slot_index = global_status->number_identified_players;
331        slot_index <
332        global_status->number_identified_players + allocated_slots;
333        slot_index++)
334     {
335       /* Set the address for identified player */
336       global_status->input_mapping[slot_index].address.port =
337 	global_status->current_packet->address.port;
338 
339       global_status->input_mapping[slot_index].address.host =
340 	global_status->current_packet->address.host;
341 
342 
343       /* Set the remote input device number */
344       global_status->input_mapping[slot_index].remote_input_device =
345 	global_status->current_packet->
346 	data[PACKET_IDENTIFICATION_INPUT_DEVICE_INDEX + slot_index -
347 	     global_status->number_identified_players];
348 
349       /* Change status from UNIDENTIFIED */
350       global_status->player_status[slot_index] = WAITING;
351     }
352 
353   global_status->allocation_request[global_status->number_allocation_request].
354     address.port = global_status->current_packet->address.port;
355 
356   global_status->allocation_request[global_status->number_allocation_request].
357     address.host = global_status->current_packet->address.host;
358 
359   global_status->allocation_request[global_status->number_allocation_request].
360     allocated_slots = allocated_slots;
361 
362   global_status->number_allocation_request++;
363 
364   global_status->number_identified_players += allocated_slots;
365 
366   send_identification_acknowledge_packet (global_status, allocated_slots);
367 
368 }
369 
370 
371 /*!
372  * Read an incoming packet and stores it for future computations
373  * \return non null if a packet was read
374  * \return 0 if no packet is available
375  */
376 int
read_incoming_server_packet(global_status_type * global_status)377 read_incoming_server_packet (global_status_type * global_status)
378 {
379   switch (SDLNet_UDP_Recv
380 	  (global_status->server_socket, global_status->current_packet))
381     {
382     case 0:
383       return 0;
384     case 1:
385 #if defined(DEBUG)
386       fprintf (stderr, "Stored a packet from client.\n");
387 #endif
388       return 1;
389     default:
390       fprintf (stderr, "Internal warning: impossible case (%s:%s)\n",
391 	       __FILE__, __LINE__);
392       return 0;
393     }
394   return 0;
395 }
396 
397 /*!
398  * Process a status packet and updates global input status and client status
399  */
400 void
store_remote_status_lan(global_status_type * global_status,global_option_type * global_option)401 store_remote_status_lan (global_status_type * global_status,
402 			 global_option_type * global_option)
403 {
404   int input_index;
405 
406   if (global_status->current_packet->len != PACKET_STATUS_LENGTH)
407     {
408       /* Discarding invalid packet */
409 #if defined(DEBUG)
410       fprintf (stderr,
411 	       "Invalid packet received when in status receiving phase (received length: %d, expected length: %d)\n",
412 	       global_status->current_packet->len, PACKET_STATUS_LENGTH);
413 #endif
414 
415       /* If may be an identification packet which was lost at some point */
416       identify_client (global_status, global_option);
417 
418       return;
419     }
420 
421   if (global_status->current_packet->data[PACKET_IDENTIFIER] !=
422       PACKET_STATUS_ID)
423     {
424       /* Discarding invalid packet */
425 #if defined(DEBUG)
426       fprintf (stderr,
427 	       "Invalid packet received when in status receiving phase (received ID: 0x%02x, expected ID: 0x%02x)\n",
428 	       global_status->current_packet->data[PACKET_IDENTIFIER],
429 	       PACKET_STATUS_ID);
430 #endif
431       return;
432     }
433 
434   if (global_status->frame_number !=
435       SDLNet_Read32 (global_status->current_packet->data +
436 		     PACKET_STATUS_FRAME_NUMBER))
437     {
438 
439       if (global_status->frame_number ==
440 	  SDLNet_Read32 (global_status->current_packet->data +
441 			 PACKET_STATUS_FRAME_NUMBER) + 1)
442 	{
443 	  /*
444 	   * This a request for the previous frame, i.e. a client lost his digest, we resend it to him
445 	   */
446 
447 	  send_previous_digest_packet (global_status, global_option,
448 				       global_status->current_packet->
449 				       address);
450 
451 	  return;
452 	}
453 
454       /* Discarding status not corresponding to this frame */
455 #if defined(DEBUG)
456       fprintf (stderr,
457 	       "Invalid packet received when in status receiving phase (status for frame %d, awaiting for frame %d)\n",
458 	       SDLNet_Read32 (global_status->current_packet->data +
459 			      PACKET_STATUS_FRAME_NUMBER),
460 	       global_status->frame_number);
461 #endif
462       return;
463     }
464 
465   for (input_index = 0; input_index < MAX_NUMBER_PLAYER; input_index++)
466     {
467       if (equals_address
468 	  (global_status->current_packet->address,
469 	   global_status->input_mapping[input_index].address))
470 	{
471 
472 	  if (global_status->player_status[input_index] == READY)
473 	    {
474 #if defined(DEBUG)
475 	      fprintf (stderr,
476 		       "We have another status for the same frame, same origin.");
477 #endif
478 	      /* TODO : check if it's the same status we already have stored */
479 	    }
480 
481 	  /* Global input number input_index will be filled by this packet status */
482 	  global_status->input_value[input_index] =
483 	    global_status->current_packet->
484 	    data[PACKET_STATUS_INPUT_DEVICE_INDEX +
485 		 global_status->input_mapping[input_index].
486 		 remote_input_device];
487 
488 #if defined(DEBUG)
489 	  fprintf (stderr, "input value[%d] = 0x%02x (data[%d])\n",
490 		   input_index,
491 		   global_status->current_packet->
492 		   data[PACKET_STATUS_INPUT_DEVICE_INDEX +
493 			global_status->input_mapping[input_index].
494 			remote_input_device],
495 		   PACKET_STATUS_INPUT_DEVICE_INDEX +
496 		   global_status->input_mapping[input_index].
497 		   remote_input_device);
498 #endif
499 
500 	  /* Slot is now filled */
501 	  global_status->player_status[input_index] = READY;
502 
503 	}
504     }
505 
506 }
507 
508 /*!
509  * Reset the input value to begin with a new frame
510  */
511 void
init_frame_status(global_status_type * global_status,global_option_type * global_option)512 init_frame_status (global_status_type * global_status,
513 		   global_option_type * global_option)
514 {
515   int player_index;
516 
517   for (player_index = 0; player_index < global_option->number_player;
518        player_index++)
519     {
520       previous_input_value[player_index] =
521 	global_status->input_value[player_index];
522       global_status->input_value[player_index] = 0;
523       global_status->player_status[player_index] = WAITING;
524     }
525 }
526 
527 
528 /*!
529  * Send a digest packet to a single client
530  */
531 void
send_individual_digest_packet(IPaddress client_address,global_status_type * global_status)532 send_individual_digest_packet (IPaddress client_address,
533 			       global_status_type * global_status)
534 {
535   int number_destination;
536 
537   global_status->digest_packet->address = client_address;
538 
539   number_destination = SDLNet_UDP_Send (global_status->server_socket,
540 					-1, global_status->digest_packet);
541 
542   if (number_destination != 1)
543     {
544       fprintf (stderr, "Couldn't send the status packet to client\n");
545     }
546 
547 }
548 
549 /*!
550  * Send a digest packet to clients if they reeeeeeeeeally deserve it
551  */
552 void
send_digest_lan_packets(global_status_type * global_status,global_option_type * global_option)553 send_digest_lan_packets (global_status_type * global_status,
554 			 global_option_type * global_option)
555 {
556 
557   int player_index;
558 
559   for (player_index = 0; player_index < global_option->number_player;
560        player_index++)
561     {
562       if (global_status->player_status[player_index] != READY)
563 	{
564 #if defined(DEBUG)
565 	  fprintf (stderr,
566 		   "Player number %d not being READY prevent the sending of the digest packet\n",
567 		   player_index);
568 #endif
569 	  return;
570 	}
571     }
572 
573   /* If we're here, all concerned clients are READY (== we got their status) */
574 
575   /* Set the identifier */
576   global_status->digest_packet->data[PACKET_IDENTIFIER] = PACKET_DIGEST_ID;
577 
578   /* Set the current frame */
579   SDLNet_Write32 (global_status->frame_number,
580 		  global_status->digest_packet->
581 		  data + PACKET_STATUS_FRAME_NUMBER);
582 
583   /* Set the digest input values */
584   for (player_index = 0; player_index < global_option->number_player;
585        player_index++)
586     {
587       global_status->digest_packet->data[PACKET_STATUS_INPUT_DEVICE_NUMBER +
588 					 player_index] =
589 	global_status->input_value[player_index];
590     }
591 
592   /* Compute the checksum */
593   global_status->digest_packet->
594     data[PACKET_STATUS_CHECKSUM] =
595     compute_checksum (global_status->digest_packet->data, PACKET_IDENTIFIER,
596 		      PACKET_STATUS_CHECKSUM);
597 
598 
599   global_status->digest_packet->len = PACKET_STATUS_LENGTH;
600 
601   /* Now, global_status->digest_packet is THE digest packet we can send to all clients */
602 
603   for (player_index = 0; player_index < global_option->number_player;
604        player_index++)
605     {
606 
607       /* TODO : find a much more elegant and efficient way to find unique client addresses */
608 
609       int duplicate_index;
610       int already_sent;
611 
612       already_sent = 0;
613 
614       for (duplicate_index = 0; duplicate_index < player_index;
615 	   duplicate_index++)
616 	{
617 	  if (equals_address
618 	      (global_status->input_mapping[player_index].address,
619 	       global_status->input_mapping[duplicate_index].address))
620 	    {
621 	      already_sent = 1;
622 	      break;
623 	    }
624 	}
625 
626       if (!already_sent)
627 	{
628 	  send_individual_digest_packet (global_status->
629 					 input_mapping[player_index].address,
630 					 global_status);
631 	}
632 
633     }
634 
635   /* We're done with this frame, skip to the next one */
636   global_status->frame_number++;
637 
638   /* Get ready for receiving status for next (now current) frame */
639   init_frame_status (global_status, global_option);
640 
641 }
642 
643 
644 /*!
645  * Send the digest packet of the previous frame to a single client
646  */
647 void
send_previous_digest_packet(global_status_type * global_status,global_option_type * global_option,IPaddress address)648 send_previous_digest_packet (global_status_type * global_status,
649 			     global_option_type * global_option,
650 			     IPaddress address)
651 {
652 
653   int player_index;
654 
655   /* Set the identifier */
656   global_status->digest_packet->data[PACKET_IDENTIFIER] = PACKET_DIGEST_ID;
657 
658   /* Set the previous frame */
659   SDLNet_Write32 (global_status->frame_number - 1,
660 		  global_status->digest_packet->
661 		  data + PACKET_STATUS_FRAME_NUMBER);
662 
663   /* Set the digest input values */
664   for (player_index = 0; player_index < global_option->number_player;
665        player_index++)
666     {
667       global_status->digest_packet->data[PACKET_STATUS_INPUT_DEVICE_NUMBER +
668 					 player_index] =
669 	previous_input_value[player_index];
670     }
671 
672   /* Compute the checksum */
673   global_status->digest_packet->
674     data[PACKET_STATUS_CHECKSUM] =
675     compute_checksum (global_status->digest_packet->data, PACKET_IDENTIFIER,
676 		      PACKET_STATUS_CHECKSUM);
677 
678 
679   global_status->digest_packet->len = PACKET_STATUS_LENGTH;
680 
681   /* Now, global_status->digest_packet is the previous digest packet we can send to this given client */
682 
683   send_individual_digest_packet (global_status->input_mapping[player_index].
684 				 address, global_status);
685 }
686 
687 /*!
688  * Main loop of the lan protocol service
689  */
690 void
serve_clients_lan_protocol(global_status_type * global_status,global_option_type * global_option)691 serve_clients_lan_protocol (global_status_type * global_status,
692 			    global_option_type * global_option)
693 {
694 
695   init_frame_status (global_status, global_option);
696 
697   for (;;)
698     {
699       int number_ready_socket;
700 
701 #if defined(DEBUG)
702       fprintf (stderr, "Waiting for status packets for frame %u\n",
703 	       global_status->frame_number);
704 #endif
705 
706 #if defined(GTK)
707 	while (gtk_events_pending())
708 	{
709 	  if (gtk_main_iteration())
710 	  {
711 	  	return;
712 	  }
713 	}
714 
715 	if (gtk_stop_asked)
716 	{
717 		return;
718 	}
719 #endif
720 
721       number_ready_socket =
722 	SDLNet_CheckSockets (global_status->server_socket_set,
723 			     SERVER_SOCKET_TIMEOUT);
724 
725       if (number_ready_socket == -1)
726 	{
727 	  fprintf (stderr, "Error in socket waiting (disconnection ?).\n");
728 	  break;
729 	}
730 
731       if (number_ready_socket == 1)
732 	{
733 
734 #if defined(DEBUG)
735 	  fprintf (stderr, "Got a packet\n");
736 #endif
737 
738 	  /* We're awaiting a packet in the server socket */
739 	  if (read_incoming_server_packet (global_status))
740 	    {
741 	      /* Stores the incoming status and updates clients status accordingly */
742 	      store_remote_status_lan (global_status, global_option);
743 
744 	      /* Sends packet (if needed) to client accordingly to their status */
745 	      send_digest_lan_packets (global_status, global_option);
746 	    }
747 
748 	}
749 
750     }
751 }
752 
753 /*!
754  * Return the time remaining before the next timeout to which issue a digest to all clients
755  * \param global_status the global status of the server
756  */
757 int
get_remaining_time(global_status_type * global_status)758 get_remaining_time (global_status_type * global_status)
759 {
760   double next_time;
761   UInt32 current_time;
762 
763   current_time = SDL_GetTicks ();
764 
765   next_time =
766     global_status->start_time +
767     global_status->frame_number * DOUBLE_FRAME_DURATION;
768 
769 #if defined(DEBUG)
770   fprintf (stderr,
771 	   "Current time : %u ticks (%f second(s))\nTime up next frame (frame %d) : %u ticks (%f second(s))\n",
772 	   current_time,
773 	   (double) current_time / (double) SDL_TICKS_PER_SECOND,
774 	   global_status->frame_number,
775 	   (UInt32) (next_time * 1000.0) - current_time,
776 	   next_time - current_time / (double) SDL_TICKS_PER_SECOND);
777 
778 #endif
779 
780   if ((UInt32) (next_time * 1000.0) < current_time)
781     {
782       fprintf (stderr, "The server is too slow, we're late for frame %u\n",
783 	       global_status->frame_number);
784       return 0;
785     }
786   return (UInt32) (next_time * 1000.0) - current_time;
787 }
788 
789 /*!
790  * Store an individual input value
791  */
792 void
store_individual_status(global_status_type * global_status,int input_index,UChar input_value)793 store_individual_status (global_status_type * global_status,
794 			 int input_index, UChar input_value)
795 {
796   global_status->input_value[input_index] = input_value;
797 }
798 
799 /*!
800  * Store a packet information and updates the current status as well as the next frame asked by the client
801  */
802 void
store_remote_status_internet(global_status_type * global_status,global_option_type * global_option)803 store_remote_status_internet (global_status_type * global_status,
804 			      global_option_type * global_option)
805 {
806   int input_index;
807 
808   if (global_status->current_packet->len != PACKET_STATUS_LENGTH)
809     {
810       /* Discarding invalid packet */
811 #if defined(DEBUG)
812       fprintf (stderr,
813 	       "Invalid packet received when in status receiving phase (received length: %d, expected length: %d)\n",
814 	       global_status->current_packet->len, PACKET_STATUS_LENGTH);
815 #endif
816 
817       /* If may be an identification packet which was lost at some point */
818       identify_client (global_status, global_option);
819 
820       return;
821     }
822 
823   if (global_status->current_packet->data[PACKET_IDENTIFIER] !=
824       PACKET_STATUS_ID)
825     {
826       /* Discarding invalid packet */
827 #if defined(DEBUG)
828       fprintf (stderr,
829 	       "Invalid packet received when in status receiving phase (received ID: 0x%02x, expected ID: 0x%02x)\n",
830 	       global_status->current_packet->data[PACKET_IDENTIFIER],
831 	       PACKET_STATUS_ID);
832 #endif
833       return;
834     }
835 
836   /* TODO : add checksum testing */
837 
838   for (input_index = 0; input_index < MAX_NUMBER_PLAYER; input_index++)
839     {
840       if (equals_address
841 	  (global_status->current_packet->address,
842 	   global_status->input_mapping[input_index].address))
843 	{
844 
845 	  /* Update the status with the client status */
846 	  store_individual_status (global_status, input_index,
847 				   global_status->current_packet->
848 				   data[PACKET_STATUS_INPUT_DEVICE_INDEX +
849 					global_status->
850 					input_mapping[input_index].
851 					remote_input_device]);
852 
853 	  if (SDLNet_Read32
854 	      (global_status->current_packet->data +
855 	       PACKET_STATUS_FRAME_NUMBER) != 0)
856 	    {
857 
858 	      /* The client requested a given frame */
859 
860 	      /* Update the next frame to send to this player */
861 	      global_status->next_frame_asked[input_index] =
862 		SDLNet_Read32 (global_status->current_packet->data +
863 			       PACKET_STATUS_FRAME_NUMBER);
864 
865 #if defined(DEBUG)
866 	      if (global_status->next_frame_asked[input_index] ==
867 		  global_status->frame_number)
868 		{
869 		  fprintf (stderr,
870 			   "Great, player is waiting for the current frame, lag is less than a frame delay (player %d).\n",
871 			   input_index);
872 		}
873 
874 	      if (global_status->next_frame_asked[input_index] ==
875 		  global_status->next_frame_to_send[input_index])
876 		{
877 		  fprintf (stderr,
878 			   "Half great, we don't have packets loss, the game should be smooth (player %d).\n",
879 			   input_index);
880 		}
881 
882 #endif
883 
884 	      if (global_status->next_frame_asked[input_index] >
885 		  global_status->frame_number)
886 		{
887 		  fprintf (stderr,
888 			   "Error, player asked a frame which hasn't been reached yet! (asked: %d, maximum: %d)\n",
889 			   global_status->next_frame_asked[input_index],
890 			   global_status->frame_number);
891 		  global_status->next_frame_asked[input_index] =
892 		    global_status->frame_number;
893 
894 		}
895 
896 	      if ((global_status->frame_number > AUTO_OUTDATE)
897 		  && (global_status->next_frame_asked[input_index] <=
898 		      global_status->frame_number - AUTO_OUTDATE))
899 		{
900 		  fprintf (stderr,
901 			   "FATAL ERROR !! Player requested an outdated frame, we can't provide it! (requested : %d, available range : %d - %d)\n",
902 			   global_status->next_frame_asked[input_index],
903 			   global_status->frame_number - AUTO_OUTDATE,
904 			   global_status->frame_number);
905 
906 		  /* TODO: think about actions which should be performed in this case. */
907 		}
908 
909 	      /* Update the counter for sending frames */
910 	      global_status->next_frame_to_send[input_index] =
911 		global_status->next_frame_asked[input_index];
912 	    }
913 	  else
914 	    {
915 #if defined(DEBUG)
916 	      fprintf (stderr,
917 		       "Client sends a status without requesting a specific frame.\n");
918 #endif
919 	    }
920 	}
921     }
922 }
923 
924 /*!
925  * Allocate memory for the history
926  */
927 void
init_internet_history()928 init_internet_history ()
929 {
930   if (history_digest != NULL)
931     {
932       fprintf (stderr, "Attempting to reallocate the history.\n");
933       return;
934     }
935 
936   history_digest = (UChar *) malloc ((size_t) BUFFER_SIZE_BYTES);
937 
938   if (history_digest == NULL)
939     {
940       fprintf (stderr,
941 	       "FATAL ERROR ! Couldn't allocate memory for the history. Exiting.\n");
942       exit (2);
943     }
944 }
945 
946 /*!
947  * Free memory for the history
948  */
949 void
release_internet_history()950 release_internet_history ()
951 {
952   if (history_digest == NULL)
953     {
954       fprintf (stderr, "Attempting to free non allocated history.\n");
955       return;
956     }
957 
958   free (history_digest);
959 
960 }
961 
962 /*!
963  * Push the current frame status in the history
964  */
965 void
store_current_status_in_history(global_status_type * global_status)966 store_current_status_in_history (global_status_type * global_status)
967 {
968   memcpy (history_digest +
969 	  (global_status->frame_number % AUTO_OUTDATE) * DIGEST_SIZE,
970 	  global_status->input_value, DIGEST_SIZE);
971 }
972 
973 /*!
974  * Send a packet to a client to fit the need in term of frame number(s)
975  * \param global_status global status for the application
976  * \param client_index index of the global player to which send a packet
977  */
978 void
send_individual_internet_digest_packet(global_status_type * global_status,int client_index)979 send_individual_internet_digest_packet (global_status_type * global_status,
980 					int client_index)
981 {
982   unsigned int frame_number;
983   unsigned int frame_number_start;
984   int number_digest;
985   int digest_index;
986   int number_destination;
987 
988   /* Set the identifier */
989   global_status->digest_packet->data[PACKET_IDENTIFIER] =
990     PACKET_INTERNET_DIGEST_ID;
991 
992   /* Set the number of the first frame digest */
993   SDLNet_Write32 (global_status->next_frame_to_send[client_index],
994 		  global_status->digest_packet->data +
995 		  PACKET_INTERNET_DIGEST_FRAME_NUMBER);
996 
997   /* Compute the number of digest to send */
998   number_digest =
999     min (PACKET_INTERNET_DIGEST_DIGEST_NUMBER, global_status->frame_number);
1000 
1001   if (number_digest < PACKET_INTERNET_DIGEST_DIGEST_NUMBER)
1002     {
1003       /* We're at the beginning of the game */
1004       frame_number_start = 1;
1005     }
1006   else
1007     {
1008       if (global_status->next_frame_to_send[client_index] + number_digest >
1009 	  global_status->frame_number)
1010 	{
1011 	  /*
1012 	   * If we feed the client with frames between what is expected and what we can provide at max, we won't fill all
1013 	   * the space we have. Let's then feed the maximum of space up to what we can provide (thus giving frames which are
1014 	   * thought to be older than what the client is waiting for [but it could be false and effectively waiting them])
1015 	   */
1016 	  frame_number_start =
1017 	    global_status->frame_number -
1018 	    PACKET_INTERNET_DIGEST_DIGEST_NUMBER + 1;
1019 	}
1020       else
1021 	{
1022 	  /*
1023 	   * We can fill the whole space with data from what was requested and we won't reach the last frame for which
1024 	   * we can provide the status
1025 	   */
1026 	  frame_number_start =
1027 	    global_status->next_frame_to_send[client_index];
1028 	}
1029     }
1030 
1031 #if defined(DEBUG)
1032   fprintf (stderr,
1033 	   "number of digests to send = %d (%d-%d)\nnext_frame_to_send = %d, frame_number = %d\n",
1034 	   number_digest,
1035 	   frame_number_start,
1036 	   frame_number_start + number_digest - 1,
1037 	   global_status->next_frame_to_send[client_index],
1038 	   global_status->frame_number);
1039 #endif
1040 
1041   /* Set the number of digest to send */
1042   global_status->digest_packet->data[PACKET_INTERNET_DIGEST_NUMBER_DIGEST] =
1043     number_digest;
1044 
1045   global_status->digest_packet->len =
1046     PACKET_INTERNET_DIGEST_BASE_LENGTH +
1047     number_digest * PACKET_INTERNET_DIGEST_INCREMENT_LENGTH;
1048 
1049   /* Set destination address */
1050   global_status->digest_packet->address =
1051     global_status->input_mapping[client_index].address;
1052 
1053   for (digest_index = 0, frame_number = frame_number_start;
1054        digest_index < number_digest; digest_index++, frame_number++)
1055     {
1056       memcpy (global_status->digest_packet->data +
1057 	      PACKET_INTERNET_DIGEST_DIGEST_INDEX +
1058 	      digest_index * DIGEST_SIZE,
1059 	      history_digest + (frame_number % AUTO_OUTDATE) * DIGEST_SIZE,
1060 	      DIGEST_SIZE);
1061     }
1062 
1063   /* The checksum is located after the last digest data */
1064   global_status->digest_packet->data[PACKET_INTERNET_DIGEST_DIGEST_INDEX +
1065 				     number_digest * DIGEST_SIZE] =
1066     compute_checksum (global_status->digest_packet->data, PACKET_IDENTIFIER,
1067 		      PACKET_INTERNET_DIGEST_DIGEST_INDEX +
1068 		      number_digest * DIGEST_SIZE);
1069 
1070   number_destination = SDLNet_UDP_Send (global_status->server_socket,
1071 					-1, global_status->digest_packet);
1072 
1073   if (number_destination != 1)
1074     {
1075       fprintf (stderr, "Couldn't send the digest packet to client\n");
1076     }
1077 
1078   /* Next time, we'll send the next frame we haven't send yet */
1079   global_status->next_frame_to_send[client_index] = frame_number;
1080 
1081 }
1082 
1083 /*!
1084  * Send a digest packet to all clients
1085  */
1086 void
send_internet_digest(global_status_type * global_status,global_option_type * global_option)1087 send_internet_digest (global_status_type * global_status,
1088 		      global_option_type * global_option)
1089 {
1090 
1091   int player_index;
1092   int client_index;
1093 
1094   /* Store the current status in the history */
1095   store_current_status_in_history (global_status);
1096 
1097   for (player_index = 0, client_index = 0;
1098        player_index < global_option->number_player; player_index++)
1099     {
1100 
1101       /* TODO : find a much more elegant and efficient way to find unique client addresses */
1102 
1103       int duplicate_index;
1104       int already_sent;
1105 
1106       already_sent = 0;
1107 
1108       for (duplicate_index = 0; duplicate_index < player_index;
1109 	   duplicate_index++)
1110 	{
1111 	  if (equals_address
1112 	      (global_status->input_mapping[player_index].address,
1113 	       global_status->input_mapping[duplicate_index].address))
1114 	    {
1115 	      already_sent = 1;
1116 	      break;
1117 	    }
1118 	}
1119 
1120       if (!already_sent)
1121 	{
1122 	  send_individual_internet_digest_packet (global_status,
1123 						  client_index++);
1124 	}
1125 
1126     }
1127 
1128   /* Advancing the frame number */
1129   global_status->frame_number++;
1130 
1131 }
1132 
1133 /*!
1134  * Main loop of the internet protocol service
1135  */
1136 void
serve_clients_internet_protocol(global_status_type * global_status,global_option_type * global_option)1137 serve_clients_internet_protocol (global_status_type * global_status,
1138 				 global_option_type * global_option)
1139 {
1140 
1141   int player_index;
1142 
1143   for (player_index = 0; player_index < MAX_NUMBER_PLAYER; player_index++)
1144     {
1145       global_status->next_frame_to_send[player_index] = 1;
1146       global_status->input_value[player_index] = 0;
1147     }
1148 
1149   /* Allocate memory for the history */
1150   init_internet_history ();
1151 
1152   /* Prepare memory releasing at the end of the program */
1153   atexit (release_internet_history);
1154 
1155   init_frame_status (global_status, global_option);
1156 
1157   global_status->start_time =
1158     (double) SDL_GetTicks () / (double) SDL_TICKS_PER_SECOND;
1159 
1160   for (;;)
1161     {
1162 
1163 #if defined(DEBUG)
1164       fprintf (stderr,
1165 	       "Waiting for status packets while preparing frame %u\ntime = %u\n",
1166 	       global_status->frame_number, SDL_GetTicks ());
1167 #endif
1168 
1169 #if defined(GTK)
1170 	while (gtk_events_pending())
1171 	{
1172 	  if (gtk_main_iteration())
1173 	  {
1174 	  	return;
1175 	  }
1176 	}
1177 
1178 	if (gtk_stop_asked)
1179 	{
1180 		return;
1181 	}
1182 #endif
1183 
1184       /* Let packets accumulate for a short amount of time */
1185       SDL_Delay (get_remaining_time (global_status));
1186 
1187 #if defined(DEBUG)
1188       fprintf (stderr, "Finished sleeping, time = %u\n", SDL_GetTicks ());
1189 #endif
1190 
1191       while (read_incoming_server_packet (global_status) != 0)
1192 	{
1193 
1194 #if defined(DEBUG)
1195 	  fprintf (stderr, "Got a packet\n");
1196 #endif
1197 
1198 	  /* Stores the incoming status */
1199 	  store_remote_status_internet (global_status, global_option);
1200 	}
1201 
1202       send_internet_digest (global_status, global_option);
1203 
1204     }
1205 
1206   /* While the above loop is infinite, this won't be called */
1207   release_internet_history ();
1208 
1209 }
1210