2 * $Logfile: /Freespace2/code/Network/multilag.cpp $
8 * Revision 1.4 2002/05/26 20:49:54 theoddone33
11 * Revision 1.3 2002/05/26 20:22:48 theoddone33
12 * Most of network/ works
14 * Revision 1.2 2002/05/07 03:16:47 theoddone33
15 * The Great Newline Fix
17 * Revision 1.1.1.1 2002/05/03 03:28:10 root
21 * 4 11/19/98 8:03a Dave
22 * Full support for D3-style reliable sockets. Revamped packet lag/loss
23 * system, made it receiver side and at the lowest possible level.
25 * 3 11/17/98 11:12a Dave
26 * Removed player identification by address. Now assign explicit id #'s.
28 * 2 10/07/98 10:53a Dave
31 * 1 10/07/98 10:50a Dave
33 * 15 6/05/98 9:54a Lawrance
36 * 14 4/27/98 6:02p Dave
37 * Modify how missile scoring works. Fixed a team select ui bug. Speed up
38 * multi_lag system. Put in new main hall.
40 * 13 4/18/98 5:00p Dave
41 * Put in observer zoom key. Made mission sync screen more informative.
43 * 12 4/06/98 12:37a Lawrance
44 * fix compile bug with demo
46 * 11 4/04/98 8:42p Dave
47 * Tested and debugged UDP reliable socket layer. Modified lag system to
48 * take this into account.
50 * 10 4/02/98 6:29p Lawrance
51 * compile out multilag code for demo
53 * 9 3/14/98 2:48p Dave
54 * Cleaned up observer joining code. Put in support for file xfers to
55 * ingame joiners (observers or not). Revamped and reinstalled pseudo
58 * 8 1/11/98 10:03p Allender
59 * removed <winsock.h> from headers which included it. Made psnet_socket
60 * type which is defined just as SOCKET type is.
62 * 7 12/29/97 5:21p Dave
63 * Put in object update sequencing for multiplayer.
65 * 6 12/16/97 6:17p Dave
66 * Put in primary weapon support for multiplayer weapon select screen.
68 * 5 12/10/97 4:45p Dave
69 * Added in more detailed support for multiplayer packet lag/loss. Fixed
70 * some multiplayer stuff. Added some controls to the standalone.
72 * 4 12/01/97 4:59p Dave
73 * Synchronized multiplayer debris objects. Put in pilot popup in main
74 * hall. Optimized simulated multiplayer lag module. Fixed a potential
77 * 3 11/28/97 7:04p Dave
78 * Emergency checkin due to big system crash.
80 * 2 11/28/97 5:06p Dave
81 * Put in facilities for simulating multiplayer lag.
83 * 1 11/28/97 4:38p Dave
94 #include <sys/types.h>
95 #include <sys/socket.h>
96 #include <netinet/in.h>
97 #include <arpa/inet.h>
103 #include "multilag.h"
106 #include "linklist.h"
108 // ----------------------------------------------------------------------------------------------------
109 // LAGLOSS DEFINES/VARS
112 // default LAGLOSS values
113 #define MULTI_LAGLOSS_DEF_LAG (-1)
114 #define MULTI_LAGLOSS_DEF_LAGMIN (-1)
115 #define MULTI_LAGLOSS_DEF_LAGMAX (-1)
116 #define MULTI_LAGLOSS_DEF_LOSS (-1.0f)
117 #define MULTI_LAGLOSS_DEF_LOSSMIN (-1.0f)
118 #define MULTI_LAGLOSS_DEF_LOSSMAX (-1.0f)
119 #define MULTI_LAGLOSS_DEF_STREAK (2500)
122 int Multi_lag_inited = 0;
124 // lag values (base - max and min)
125 int Multi_lag_base = -1;
126 int Multi_lag_min = -1;
127 int Multi_lag_max = -1;
129 // packet loss values (base - max and min)
130 float Multi_loss_base = -1.0f;
131 float Multi_loss_min = -1.0f;
132 float Multi_loss_max = -1.0f;
134 // streaks for lagging
135 int Multi_streak_stamp = -1; // timestamp telling when the streak of a certain lag is done
136 int Multi_streak_time = 0; // how long each streak will last
137 int Multi_current_streak = -1; // what lag the current streak has
139 // struct for buffering stuff on receives
140 typedef struct lag_buf {
141 ubyte data[700]; // the data from the packet
142 int data_len; // length of the data
143 uint socket; // this can be either a PSNET_SOCKET or a PSNET_SOCKET_RELIABLE
144 int stamp; // when this expires, make this packet available
145 SOCKADDR_IN ip_addr; // ip address when in TCP
147 SOCKADDR_IPX ipx_addr; // ipx address when in IPX mode
150 struct lag_buf * prev; // prev in the list
151 struct lag_buf * next; // next in the list
154 // lag buffers - malloced
156 #define MAX_LAG_BUFFERS 1 // only 1 buffer in non-debug builds
158 #define MAX_LAG_BUFFERS 1000
160 lag_buf *Lag_buffers[MAX_LAG_BUFFERS];
161 int Lag_buf_count = 0; // how many lag_buf's are currently in use
163 lag_buf Lag_free_list;
164 lag_buf Lag_used_list;
167 // ----------------------------------------------------------------------------------------------------
168 // LAGLOSS FORWARD DECLARATIONS
171 // get a value to lag a packet with (in ms)
172 int multi_lag_get_random_lag();
174 // boolean yes or no - should this packet be lost?
175 int multi_lag_should_be_lost();
177 // get a free packet buffer, return NULL on fail
178 lag_buf *multi_lag_get_free();
180 // put a lag buffer back
181 void multi_lag_put_free(lag_buf *buf);
183 // ----------------------------------------------------------------------------------------------------
187 void multi_lag_init()
189 // never do lag in a non-debug build
190 #if defined(NDEBUG) || !defined(MULTI_USE_LAG)
191 Multi_lag_inited = 0;
195 // if we're already inited, don't do anything
196 if(Multi_lag_inited){
200 // try and allocate lag bufs
201 for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
202 Lag_buffers[idx] = (lag_buf*)malloc(sizeof(lag_buf));
203 if(Lag_buffers[idx] == NULL){
208 // initialize lag buffer lists
209 list_init( &Lag_free_list );
210 list_init( &Lag_used_list );
212 // Link all object slots into the free list
213 for (idx=0; idx<MAX_LAG_BUFFERS; idx++) {
214 list_append(&Lag_free_list, Lag_buffers[idx]);
217 // set the default lag values
218 Multi_lag_base = MULTI_LAGLOSS_DEF_LAG;
219 Multi_lag_min = MULTI_LAGLOSS_DEF_LAGMIN;
220 Multi_lag_max = MULTI_LAGLOSS_DEF_LAGMAX;
222 // set the default loss values
223 Multi_loss_base = MULTI_LAGLOSS_DEF_LOSS;
224 Multi_loss_min = MULTI_LAGLOSS_DEF_LOSSMIN;
225 Multi_loss_max = MULTI_LAGLOSS_DEF_LOSSMAX;
227 // set the default lag streak time
228 Multi_streak_time = MULTI_LAGLOSS_DEF_STREAK;
230 Multi_lag_inited = 1;
234 void multi_lag_close()
238 // if we're not inited already, don't do anything
239 if(!Multi_lag_inited){
243 // free up lag buffers
244 for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
245 if(Lag_buffers[idx] != NULL){
246 free(Lag_buffers[idx]);
247 Lag_buffers[idx] = NULL;
251 Multi_lag_inited = 0;
254 // select for multi_lag
255 int multi_lag_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *except_fds, timeval *timeout)
264 SOCKADDR_IPX ipx_addr;
267 lag_buf *moveup, *item;
269 Assert(readfds != NULL);
270 Assert(writefds == NULL);
271 Assert(except_fds == NULL);
273 // clear out addresses
274 memset(&ip_addr, 0, sizeof(SOCKADDR_IN));
276 memset(&ipx_addr, 0, sizeof(SOCKADDR_IPX));
279 // if there's data on the socket, read it
280 if(select(nfds, readfds, writefds, except_fds, timeout)){
281 // read the data and stuff it
283 t_from_len = sizeof(SOCKADDR_IN);
284 ret_val = recvfrom(readfds->fd_array[0], t_buf, 1024, 0, (SOCKADDR*)&ip_addr, &t_from_len);
287 t_from_len = sizeof(SOCKADDR_IPX);
288 ret_val = recvfrom(readfds->fd_array[0], t_buf, 1024, 0, (SOCKADDR*)&ipx_addr, &t_from_len);
292 // wacky socket error
293 if(ret_val == SOCKET_ERROR){
297 // if we should be dropping this packet
298 if(!multi_lag_should_be_lost()){
299 // get a free packet buf and stuff the data
300 item = multi_lag_get_free();
302 Assert(ret_val < 700);
303 memcpy(item->data, t_buf, ret_val);
304 item->data_len = ret_val;
305 item->ip_addr = ip_addr;
307 item->ipx_addr = ipx_addr;
309 item->socket = readfds->fd_array[0];
310 item->stamp = timestamp(multi_lag_get_random_lag());
315 // always unset the readfds
316 readfds->fd_count = 0;
318 // now determine if we have any pending packets - find the first one
319 // NOTE : this _could_ be the packet we just read. In fact, with a 0 lag, this will always be the case
320 moveup=GET_FIRST(&Lag_used_list);
321 while ( moveup!=END_OF_LIST(&Lag_used_list) ) {
322 // if the timestamp has elapsed and we have a matching socket
323 if((readfds->fd_array[0] == (SOCKET)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
324 // set this so we think select returned yes
325 readfds->fd_count = 1;
329 moveup = GET_NEXT(moveup);
337 // recvfrom for multilag
338 int multi_lag_recvfrom(uint s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
340 lag_buf *moveup = NULL;
341 lag_buf *item = NULL;
343 // now determine if we have any pending packets - find the first one
344 moveup=GET_FIRST(&Lag_used_list);
345 while ( moveup!=END_OF_LIST(&Lag_used_list) ) {
346 // if the timestamp has elapsed
347 if((s == (SOCKET)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
352 moveup = GET_NEXT(moveup);
355 // if this happens, it means that the multi_lag_select() returned an improper value
358 Assert(item->data_len <= len);
359 memcpy(buf, item->data, item->data_len);
361 memcpy(from, &item->ip_addr, sizeof(SOCKADDR_IN));
364 memcpy(from, &item->ipx_addr, sizeof(SOCKADDR_IPX));
368 // stick the item back on the free list
369 multi_lag_put_free(item);
371 // return the size in bytes
372 return item->data_len;
375 // ----------------------------------------------------------------------------------------------------
376 // LAGLOSS FORWARD DEFINITIONS
379 int multi_lag_get_random_lag()
381 // first determine the percentage we'll be checking against
385 // if the lag system isn't inited, don't do anything (no lag)
386 if(!Multi_lag_inited){
391 // see if we should be going up or down (loss max/loss min)
393 if((float)rand()/(float)RAND_MAX < 0.5){
395 if(Multi_lag_min >= 0){
396 mod = - (int)((float)(Multi_lag_base - Multi_lag_min) * ((float)rand()/(float)RAND_MAX));
400 if(Multi_lag_max >= 0){
401 mod = (int)((float)(Multi_lag_max - Multi_lag_base) * ((float)rand()/(float)RAND_MAX));
405 // if the current streak has elapsed, calculate a new one
406 if((Multi_streak_stamp == -1) || (timestamp_elapsed(Multi_streak_stamp))){
407 // timestamp the new streak
408 Multi_streak_stamp = timestamp(Multi_streak_time);
410 // set the return value
411 ret = Multi_lag_base + mod;
413 // set the lag value of this current streak
414 Multi_current_streak = ret;
416 // otherwise use the lag for the current streak
418 ret = Multi_current_streak;
424 // this _may_ be a bit heavyweight, but it _is_ debug code
425 int multi_lag_should_be_lost()
427 // first determine the percentage we'll be checking against
430 // if the lag system isn't inited, don't do anything
431 if(!Multi_lag_inited){
435 // see if we should be going up or down (loss max/loss min)
437 if((float)rand()/(float)RAND_MAX < 0.5){
439 if(Multi_loss_min >= 0.0f){
440 mod = - ((Multi_loss_base - Multi_loss_min) * ((float)rand()/(float)RAND_MAX));
444 if(Multi_loss_max >= 0.0f){
445 mod = ((Multi_loss_max - Multi_loss_base) * ((float)rand()/(float)RAND_MAX));
449 if((float)rand()/(float)RAND_MAX <= Multi_loss_base + mod){
456 // get a free packet buffer, return NULL on fail
457 lag_buf *multi_lag_get_free()
461 // if we're out of buffers
462 if(Lag_buf_count >= MAX_LAG_BUFFERS){
463 nprintf(("Network", "Out of lag buffers!\n"));
468 lagp = GET_FIRST(&Lag_free_list);
469 Assert( lagp != &Lag_free_list ); // shouldn't have the dummy element
471 // remove trailp from the free list
472 list_remove( &Lag_free_list, lagp );
474 // insert trailp onto the end of used list
475 list_append( &Lag_used_list, lagp );
477 // increase the count
482 // put a lag buffer back
483 void multi_lag_put_free(lag_buf *buf)
485 // remove objp from the used list
486 list_remove( &Lag_used_list, buf);
488 // add objp to the end of the free
489 list_append( &Lag_free_list, buf );
495 void multi_lagloss_dcf()
497 // if the lag system isn't inited, don't do anything
498 if(!Multi_lag_inited){
499 dc_printf("Lag System Not Initialized!\n");
503 // display all available commands
504 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");
506 // display lag settings
508 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);
510 // display loss settings
511 dc_printf("Loss : ");
512 dc_printf("\n Base %f\n Min %f\n Max %f\n", Multi_loss_base, Multi_loss_min, Multi_loss_max);
517 // if the lag system isn't inited, don't do anything
518 if(!Multi_lag_inited){
519 dc_printf("Lag System Not Initialized!\n");
524 // parse the argument and change things around accordingly
525 if(Dc_arg_type & ARG_INT){
527 // switch the lag sim off
531 dc_printf("Turning simulated lag off\n");
533 } else if((Multi_lag_max >= 0) && (Dc_arg_int > Multi_lag_max)){
534 dc_printf("Base value greater than max value, ignoring...");
535 } else if((Multi_lag_min >= 0) && (Dc_arg_int < Multi_lag_min)){
536 dc_printf("Base value smaller than min value, ignoring...");
538 Multi_lag_base = Dc_arg_int;
546 // if the lag system isn't inited, don't do anything
547 if(!Multi_lag_inited){
548 dc_printf("Lag System Not Initialized!\n");
553 // parse the argument and change things around accordingly
554 if(Dc_arg_type & ARG_INT){
555 if(Dc_arg_int > Multi_lag_base){
556 dc_printf("Min value greater than base value, ignoring...");
561 Multi_lag_min = Dc_arg_int;
570 // if the lag system isn't inited, don't do anything
571 if(!Multi_lag_inited){
572 dc_printf("Lag System Not Initialized!\n");
576 // parse the argument and change things around accordingly
578 if(Dc_arg_type & ARG_INT){
579 if((Dc_arg >=0) && (Dc_arg_int < Multi_lag_base)){
580 dc_printf("Max value smaller than base value, ignoring...");
585 Multi_lag_max = Dc_arg_int;
594 // if the lag system isn't inited, don't do anything
595 if(!Multi_lag_inited){
596 dc_printf("Lag System Not Initialized!\n");
600 // parse the argument and change things around accordingly
602 if(Dc_arg_type & ARG_INT){
603 float val = (float)Dc_arg_int / 100.0f;
605 if(Dc_arg_int > 100){
606 dc_printf("Illegal loss base value, ignoring...");
607 } else if(Dc_arg_int < 0){
608 // switch the loss sim off
609 dc_printf("Turning simulated loss off\n");
610 Multi_loss_base = -1.0f;
611 Multi_loss_min = -1.0f;
612 Multi_loss_max = -1.0f;
614 } else if((Multi_loss_max >= 0.0f) && (val > Multi_loss_max)){
615 dc_printf("Base value greater than max value, ignoring...");
616 } else if((Multi_loss_min >= 0.0f) && (val < Multi_loss_min)){
617 dc_printf("Base value smaller than min value, ignoring...");
619 Multi_loss_base = val;
627 // if the lag system isn't inited, don't do anything
628 if(!Multi_lag_inited){
629 dc_printf("Lag System Not Initialized!\n");
633 // parse the argument and change things around accordingly
635 if(Dc_arg_type & ARG_INT){
636 float val = (float)Dc_arg_int / 100.0f;
638 if(val > Multi_loss_base){
639 dc_printf("Min value greater than base value, ignoring...");
641 // otherwise set the value
643 Multi_loss_min = -1.0f;
645 Multi_loss_min = val;
654 // if the lag system isn't inited, don't do anything
655 if(!Multi_lag_inited){
656 dc_printf("Lag System Not Initialized!\n");
660 // parse the argument and change things around accordingly
662 if(Dc_arg_type & ARG_INT){
663 float val = (float)Dc_arg_int / 100.0f;
665 if(val < Multi_loss_base){
666 dc_printf("Max value smaller than base value, ignoring...");
668 // otherwise set the value
670 Multi_loss_max = -1.0f;
672 Multi_loss_min = val;
681 // if the lag system isn't inited, don't do anything
682 if(!Multi_lag_inited){
683 dc_printf("Lag System Not Initialized!\n");
692 // if the lag system isn't inited, don't do anything
693 if(!Multi_lag_inited){
694 dc_printf("Lag System Not Initialized!\n");
699 if(Dc_arg_type & ARG_INT){
701 Multi_streak_time = Dc_arg_int;
708 // if the lag system isn't inited, don't do anything
709 if(!Multi_lag_inited){
710 dc_printf("Lag System Not Initialized!\n");
714 dc_printf("Setting bad lag/loss parameters\n");
716 // set good lagloss parameters
717 Multi_lag_base = 500;
721 Multi_loss_base = 0.2f;
722 Multi_loss_min = 0.15f;
723 Multi_loss_max = 0.23f;
725 Multi_streak_time = 800;
726 Multi_streak_stamp = -1;
727 Multi_current_streak = -1;
732 // if the lag system isn't inited, don't do anything
733 if(!Multi_lag_inited){
734 dc_printf("Lag System Not Initialized!\n");
738 dc_printf("Setting avg lag/loss parameters\n");
740 // set good lagloss parameters
741 Multi_lag_base = 275;
745 Multi_loss_base = 0.15f;
746 Multi_loss_min = 0.1f;
747 Multi_loss_max = 0.20f;
749 Multi_streak_time = 900;
750 Multi_streak_stamp = -1;
751 Multi_current_streak = -1;
756 // if the lag system isn't inited, don't do anything
757 if(!Multi_lag_inited){
758 dc_printf("Lag System Not Initialized!\n");
762 dc_printf("Setting good lag/loss parameters\n");
764 // set good lagloss parameters
765 Multi_lag_base = 100;
769 Multi_loss_base = 0.08f;
770 Multi_loss_min = 0.0f;
771 Multi_loss_max = 0.1f;
773 Multi_streak_time = 1000;
774 Multi_streak_stamp = -1;
775 Multi_current_streak = -1;