2 * $Logfile: /Freespace2/code/Network/multilag.cpp $
8 * Revision 1.2 2002/05/07 03:16:47 theoddone33
9 * The Great Newline Fix
11 * Revision 1.1.1.1 2002/05/03 03:28:10 root
15 * 4 11/19/98 8:03a Dave
16 * Full support for D3-style reliable sockets. Revamped packet lag/loss
17 * system, made it receiver side and at the lowest possible level.
19 * 3 11/17/98 11:12a Dave
20 * Removed player identification by address. Now assign explicit id #'s.
22 * 2 10/07/98 10:53a Dave
25 * 1 10/07/98 10:50a Dave
27 * 15 6/05/98 9:54a Lawrance
30 * 14 4/27/98 6:02p Dave
31 * Modify how missile scoring works. Fixed a team select ui bug. Speed up
32 * multi_lag system. Put in new main hall.
34 * 13 4/18/98 5:00p Dave
35 * Put in observer zoom key. Made mission sync screen more informative.
37 * 12 4/06/98 12:37a Lawrance
38 * fix compile bug with demo
40 * 11 4/04/98 8:42p Dave
41 * Tested and debugged UDP reliable socket layer. Modified lag system to
42 * take this into account.
44 * 10 4/02/98 6:29p Lawrance
45 * compile out multilag code for demo
47 * 9 3/14/98 2:48p Dave
48 * Cleaned up observer joining code. Put in support for file xfers to
49 * ingame joiners (observers or not). Revamped and reinstalled pseudo
52 * 8 1/11/98 10:03p Allender
53 * removed <winsock.h> from headers which included it. Made psnet_socket
54 * type which is defined just as SOCKET type is.
56 * 7 12/29/97 5:21p Dave
57 * Put in object update sequencing for multiplayer.
59 * 6 12/16/97 6:17p Dave
60 * Put in primary weapon support for multiplayer weapon select screen.
62 * 5 12/10/97 4:45p Dave
63 * Added in more detailed support for multiplayer packet lag/loss. Fixed
64 * some multiplayer stuff. Added some controls to the standalone.
66 * 4 12/01/97 4:59p Dave
67 * Synchronized multiplayer debris objects. Put in pilot popup in main
68 * hall. Optimized simulated multiplayer lag module. Fixed a potential
71 * 3 11/28/97 7:04p Dave
72 * Emergency checkin due to big system crash.
74 * 2 11/28/97 5:06p Dave
75 * Put in facilities for simulating multiplayer lag.
77 * 1 11/28/97 4:38p Dave
92 // ----------------------------------------------------------------------------------------------------
93 // LAGLOSS DEFINES/VARS
96 // default LAGLOSS values
97 #define MULTI_LAGLOSS_DEF_LAG (-1)
98 #define MULTI_LAGLOSS_DEF_LAGMIN (-1)
99 #define MULTI_LAGLOSS_DEF_LAGMAX (-1)
100 #define MULTI_LAGLOSS_DEF_LOSS (-1.0f)
101 #define MULTI_LAGLOSS_DEF_LOSSMIN (-1.0f)
102 #define MULTI_LAGLOSS_DEF_LOSSMAX (-1.0f)
103 #define MULTI_LAGLOSS_DEF_STREAK (2500)
106 int Multi_lag_inited = 0;
108 // lag values (base - max and min)
109 int Multi_lag_base = -1;
110 int Multi_lag_min = -1;
111 int Multi_lag_max = -1;
113 // packet loss values (base - max and min)
114 float Multi_loss_base = -1.0f;
115 float Multi_loss_min = -1.0f;
116 float Multi_loss_max = -1.0f;
118 // streaks for lagging
119 int Multi_streak_stamp = -1; // timestamp telling when the streak of a certain lag is done
120 int Multi_streak_time = 0; // how long each streak will last
121 int Multi_current_streak = -1; // what lag the current streak has
123 // struct for buffering stuff on receives
124 typedef struct lag_buf {
125 ubyte data[700]; // the data from the packet
126 int data_len; // length of the data
127 uint socket; // this can be either a PSNET_SOCKET or a PSNET_SOCKET_RELIABLE
128 int stamp; // when this expires, make this packet available
129 SOCKADDR_IN ip_addr; // ip address when in TCP
130 SOCKADDR_IPX ipx_addr; // ipx address when in IPX mode
132 struct lag_buf * prev; // prev in the list
133 struct lag_buf * next; // next in the list
136 // lag buffers - malloced
138 #define MAX_LAG_BUFFERS 1 // only 1 buffer in non-debug builds
140 #define MAX_LAG_BUFFERS 1000
142 lag_buf *Lag_buffers[MAX_LAG_BUFFERS];
143 int Lag_buf_count = 0; // how many lag_buf's are currently in use
145 lag_buf Lag_free_list;
146 lag_buf Lag_used_list;
149 // ----------------------------------------------------------------------------------------------------
150 // LAGLOSS FORWARD DECLARATIONS
153 // get a value to lag a packet with (in ms)
154 int multi_lag_get_random_lag();
156 // boolean yes or no - should this packet be lost?
157 int multi_lag_should_be_lost();
159 // get a free packet buffer, return NULL on fail
160 lag_buf *multi_lag_get_free();
162 // put a lag buffer back
163 void multi_lag_put_free(lag_buf *buf);
165 // ----------------------------------------------------------------------------------------------------
169 void multi_lag_init()
171 // never do lag in a non-debug build
172 #if defined(NDEBUG) || !defined(MULTI_USE_LAG)
173 Multi_lag_inited = 0;
177 // if we're already inited, don't do anything
178 if(Multi_lag_inited){
182 // try and allocate lag bufs
183 for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
184 Lag_buffers[idx] = (lag_buf*)malloc(sizeof(lag_buf));
185 if(Lag_buffers[idx] == NULL){
190 // initialize lag buffer lists
191 list_init( &Lag_free_list );
192 list_init( &Lag_used_list );
194 // Link all object slots into the free list
195 for (idx=0; idx<MAX_LAG_BUFFERS; idx++) {
196 list_append(&Lag_free_list, Lag_buffers[idx]);
199 // set the default lag values
200 Multi_lag_base = MULTI_LAGLOSS_DEF_LAG;
201 Multi_lag_min = MULTI_LAGLOSS_DEF_LAGMIN;
202 Multi_lag_max = MULTI_LAGLOSS_DEF_LAGMAX;
204 // set the default loss values
205 Multi_loss_base = MULTI_LAGLOSS_DEF_LOSS;
206 Multi_loss_min = MULTI_LAGLOSS_DEF_LOSSMIN;
207 Multi_loss_max = MULTI_LAGLOSS_DEF_LOSSMAX;
209 // set the default lag streak time
210 Multi_streak_time = MULTI_LAGLOSS_DEF_STREAK;
212 Multi_lag_inited = 1;
216 void multi_lag_close()
220 // if we're not inited already, don't do anything
221 if(!Multi_lag_inited){
225 // free up lag buffers
226 for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
227 if(Lag_buffers[idx] != NULL){
228 free(Lag_buffers[idx]);
229 Lag_buffers[idx] = NULL;
233 Multi_lag_inited = 0;
236 // select for multi_lag
237 int multi_lag_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *except_fds, const timeval *timeout)
242 SOCKADDR_IPX ipx_addr;
244 lag_buf *moveup, *item;
246 Assert(readfds != NULL);
247 Assert(writefds == NULL);
248 Assert(except_fds == NULL);
250 // clear out addresses
251 memset(&ip_addr, 0, sizeof(SOCKADDR_IN));
252 memset(&ipx_addr, 0, sizeof(SOCKADDR_IPX));
254 // if there's data on the socket, read it
255 if(select(nfds, readfds, writefds, except_fds, timeout)){
256 // read the data and stuff it
258 t_from_len = sizeof(SOCKADDR_IN);
259 ret_val = recvfrom(readfds->fd_array[0], t_buf, 1024, 0, (SOCKADDR*)&ip_addr, &t_from_len);
261 t_from_len = sizeof(SOCKADDR_IPX);
262 ret_val = recvfrom(readfds->fd_array[0], t_buf, 1024, 0, (SOCKADDR*)&ipx_addr, &t_from_len);
265 // wacky socket error
266 if(ret_val == SOCKET_ERROR){
270 // if we should be dropping this packet
271 if(!multi_lag_should_be_lost()){
272 // get a free packet buf and stuff the data
273 item = multi_lag_get_free();
275 Assert(ret_val < 700);
276 memcpy(item->data, t_buf, ret_val);
277 item->data_len = ret_val;
278 item->ip_addr = ip_addr;
279 item->ipx_addr = ipx_addr;
280 item->socket = readfds->fd_array[0];
281 item->stamp = timestamp(multi_lag_get_random_lag());
286 // always unset the readfds
287 readfds->fd_count = 0;
289 // now determine if we have any pending packets - find the first one
290 // NOTE : this _could_ be the packet we just read. In fact, with a 0 lag, this will always be the case
291 moveup=GET_FIRST(&Lag_used_list);
292 while ( moveup!=END_OF_LIST(&Lag_used_list) ) {
293 // if the timestamp has elapsed and we have a matching socket
294 if((readfds->fd_array[0] == (SOCKET)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
295 // set this so we think select returned yes
296 readfds->fd_count = 1;
300 moveup = GET_NEXT(moveup);
307 // recvfrom for multilag
308 int multi_lag_recvfrom(uint s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
310 lag_buf *moveup = NULL;
311 lag_buf *item = NULL;
313 // now determine if we have any pending packets - find the first one
314 moveup=GET_FIRST(&Lag_used_list);
315 while ( moveup!=END_OF_LIST(&Lag_used_list) ) {
316 // if the timestamp has elapsed
317 if((s == (SOCKET)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
322 moveup = GET_NEXT(moveup);
325 // if this happens, it means that the multi_lag_select() returned an improper value
328 Assert(item->data_len <= len);
329 memcpy(buf, item->data, item->data_len);
331 memcpy(from, &item->ip_addr, sizeof(SOCKADDR_IN));
333 memcpy(from, &item->ipx_addr, sizeof(SOCKADDR_IPX));
336 // stick the item back on the free list
337 multi_lag_put_free(item);
339 // return the size in bytes
340 return item->data_len;
343 // ----------------------------------------------------------------------------------------------------
344 // LAGLOSS FORWARD DEFINITIONS
347 int multi_lag_get_random_lag()
349 // first determine the percentage we'll be checking against
353 // if the lag system isn't inited, don't do anything (no lag)
354 if(!Multi_lag_inited){
359 // see if we should be going up or down (loss max/loss min)
361 if((float)rand()/(float)RAND_MAX < 0.5){
363 if(Multi_lag_min >= 0){
364 mod = - (int)((float)(Multi_lag_base - Multi_lag_min) * ((float)rand()/(float)RAND_MAX));
368 if(Multi_lag_max >= 0){
369 mod = (int)((float)(Multi_lag_max - Multi_lag_base) * ((float)rand()/(float)RAND_MAX));
373 // if the current streak has elapsed, calculate a new one
374 if((Multi_streak_stamp == -1) || (timestamp_elapsed(Multi_streak_stamp))){
375 // timestamp the new streak
376 Multi_streak_stamp = timestamp(Multi_streak_time);
378 // set the return value
379 ret = Multi_lag_base + mod;
381 // set the lag value of this current streak
382 Multi_current_streak = ret;
384 // otherwise use the lag for the current streak
386 ret = Multi_current_streak;
392 // this _may_ be a bit heavyweight, but it _is_ debug code
393 int multi_lag_should_be_lost()
395 // first determine the percentage we'll be checking against
398 // if the lag system isn't inited, don't do anything
399 if(!Multi_lag_inited){
403 // see if we should be going up or down (loss max/loss min)
405 if((float)rand()/(float)RAND_MAX < 0.5){
407 if(Multi_loss_min >= 0.0f){
408 mod = - ((Multi_loss_base - Multi_loss_min) * ((float)rand()/(float)RAND_MAX));
412 if(Multi_loss_max >= 0.0f){
413 mod = ((Multi_loss_max - Multi_loss_base) * ((float)rand()/(float)RAND_MAX));
417 if((float)rand()/(float)RAND_MAX <= Multi_loss_base + mod){
424 // get a free packet buffer, return NULL on fail
425 lag_buf *multi_lag_get_free()
429 // if we're out of buffers
430 if(Lag_buf_count >= MAX_LAG_BUFFERS){
431 nprintf(("Network", "Out of lag buffers!\n"));
436 lagp = GET_FIRST(&Lag_free_list);
437 Assert( lagp != &Lag_free_list ); // shouldn't have the dummy element
439 // remove trailp from the free list
440 list_remove( &Lag_free_list, lagp );
442 // insert trailp onto the end of used list
443 list_append( &Lag_used_list, lagp );
445 // increase the count
450 // put a lag buffer back
451 void multi_lag_put_free(lag_buf *buf)
453 // remove objp from the used list
454 list_remove( &Lag_used_list, buf);
456 // add objp to the end of the free
457 list_append( &Lag_free_list, buf );
463 void multi_lagloss_dcf()
465 // if the lag system isn't inited, don't do anything
466 if(!Multi_lag_inited){
467 dc_printf("Lag System Not Initialized!\n");
471 // display all available commands
472 dc_printf("Usage :\nlag <ms> (-1 to disable)\nlag_min <ms>\nlag_max <ms>\nloss <0-100> (-1 to disable)\nloss_min <0-100>\nloss_max <0-100>\nlag_streak <ms>\nlagloss\n");
474 // display lag settings
476 dc_printf("\n Base %d\n Min %d\n Max %d\n Streak %d\n", Multi_lag_base, Multi_lag_min, Multi_lag_max, Multi_streak_time);
478 // display loss settings
479 dc_printf("Loss : ");
480 dc_printf("\n Base %f\n Min %f\n Max %f\n", Multi_loss_base, Multi_loss_min, Multi_loss_max);
485 // if the lag system isn't inited, don't do anything
486 if(!Multi_lag_inited){
487 dc_printf("Lag System Not Initialized!\n");
492 // parse the argument and change things around accordingly
493 if(Dc_arg_type & ARG_INT){
495 // switch the lag sim off
499 dc_printf("Turning simulated lag off\n");
501 } else if((Multi_lag_max >= 0) && (Dc_arg_int > Multi_lag_max)){
502 dc_printf("Base value greater than max value, ignoring...");
503 } else if((Multi_lag_min >= 0) && (Dc_arg_int < Multi_lag_min)){
504 dc_printf("Base value smaller than min value, ignoring...");
506 Multi_lag_base = Dc_arg_int;
514 // if the lag system isn't inited, don't do anything
515 if(!Multi_lag_inited){
516 dc_printf("Lag System Not Initialized!\n");
521 // parse the argument and change things around accordingly
522 if(Dc_arg_type & ARG_INT){
523 if(Dc_arg_int > Multi_lag_base){
524 dc_printf("Min value greater than base value, ignoring...");
529 Multi_lag_min = Dc_arg_int;
538 // if the lag system isn't inited, don't do anything
539 if(!Multi_lag_inited){
540 dc_printf("Lag System Not Initialized!\n");
544 // parse the argument and change things around accordingly
546 if(Dc_arg_type & ARG_INT){
547 if((Dc_arg >=0) && (Dc_arg_int < Multi_lag_base)){
548 dc_printf("Max value smaller than base value, ignoring...");
553 Multi_lag_max = Dc_arg_int;
562 // if the lag system isn't inited, don't do anything
563 if(!Multi_lag_inited){
564 dc_printf("Lag System Not Initialized!\n");
568 // parse the argument and change things around accordingly
570 if(Dc_arg_type & ARG_INT){
571 float val = (float)Dc_arg_int / 100.0f;
573 if(Dc_arg_int > 100){
574 dc_printf("Illegal loss base value, ignoring...");
575 } else if(Dc_arg_int < 0){
576 // switch the loss sim off
577 dc_printf("Turning simulated loss off\n");
578 Multi_loss_base = -1.0f;
579 Multi_loss_min = -1.0f;
580 Multi_loss_max = -1.0f;
582 } else if((Multi_loss_max >= 0.0f) && (val > Multi_loss_max)){
583 dc_printf("Base value greater than max value, ignoring...");
584 } else if((Multi_loss_min >= 0.0f) && (val < Multi_loss_min)){
585 dc_printf("Base value smaller than min value, ignoring...");
587 Multi_loss_base = val;
595 // if the lag system isn't inited, don't do anything
596 if(!Multi_lag_inited){
597 dc_printf("Lag System Not Initialized!\n");
601 // parse the argument and change things around accordingly
603 if(Dc_arg_type & ARG_INT){
604 float val = (float)Dc_arg_int / 100.0f;
606 if(val > Multi_loss_base){
607 dc_printf("Min value greater than base value, ignoring...");
609 // otherwise set the value
611 Multi_loss_min = -1.0f;
613 Multi_loss_min = val;
622 // if the lag system isn't inited, don't do anything
623 if(!Multi_lag_inited){
624 dc_printf("Lag System Not Initialized!\n");
628 // parse the argument and change things around accordingly
630 if(Dc_arg_type & ARG_INT){
631 float val = (float)Dc_arg_int / 100.0f;
633 if(val < Multi_loss_base){
634 dc_printf("Max value smaller than base value, ignoring...");
636 // otherwise set the value
638 Multi_loss_max = -1.0f;
640 Multi_loss_min = val;
649 // if the lag system isn't inited, don't do anything
650 if(!Multi_lag_inited){
651 dc_printf("Lag System Not Initialized!\n");
660 // if the lag system isn't inited, don't do anything
661 if(!Multi_lag_inited){
662 dc_printf("Lag System Not Initialized!\n");
667 if(Dc_arg_type & ARG_INT){
669 Multi_streak_time = Dc_arg_int;
676 // if the lag system isn't inited, don't do anything
677 if(!Multi_lag_inited){
678 dc_printf("Lag System Not Initialized!\n");
682 dc_printf("Setting bad lag/loss parameters\n");
684 // set good lagloss parameters
685 Multi_lag_base = 500;
689 Multi_loss_base = 0.2f;
690 Multi_loss_min = 0.15f;
691 Multi_loss_max = 0.23f;
693 Multi_streak_time = 800;
694 Multi_streak_stamp = -1;
695 Multi_current_streak = -1;
700 // if the lag system isn't inited, don't do anything
701 if(!Multi_lag_inited){
702 dc_printf("Lag System Not Initialized!\n");
706 dc_printf("Setting avg lag/loss parameters\n");
708 // set good lagloss parameters
709 Multi_lag_base = 275;
713 Multi_loss_base = 0.15f;
714 Multi_loss_min = 0.1f;
715 Multi_loss_max = 0.20f;
717 Multi_streak_time = 900;
718 Multi_streak_stamp = -1;
719 Multi_current_streak = -1;
724 // if the lag system isn't inited, don't do anything
725 if(!Multi_lag_inited){
726 dc_printf("Lag System Not Initialized!\n");
730 dc_printf("Setting good lag/loss parameters\n");
732 // set good lagloss parameters
733 Multi_lag_base = 100;
737 Multi_loss_base = 0.08f;
738 Multi_loss_min = 0.0f;
739 Multi_loss_max = 0.1f;
741 Multi_streak_time = 1000;
742 Multi_streak_stamp = -1;
743 Multi_current_streak = -1;