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