2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Network/multilag.cpp $
16 * Revision 1.5 2002/06/09 04:41:24 relnev
17 * added copyright header
19 * Revision 1.4 2002/05/26 20:49:54 theoddone33
22 * Revision 1.3 2002/05/26 20:22:48 theoddone33
23 * Most of network/ works
25 * Revision 1.2 2002/05/07 03:16:47 theoddone33
26 * The Great Newline Fix
28 * Revision 1.1.1.1 2002/05/03 03:28:10 root
32 * 4 11/19/98 8:03a Dave
33 * Full support for D3-style reliable sockets. Revamped packet lag/loss
34 * system, made it receiver side and at the lowest possible level.
36 * 3 11/17/98 11:12a Dave
37 * Removed player identification by address. Now assign explicit id #'s.
39 * 2 10/07/98 10:53a Dave
42 * 1 10/07/98 10:50a Dave
44 * 15 6/05/98 9:54a Lawrance
47 * 14 4/27/98 6:02p Dave
48 * Modify how missile scoring works. Fixed a team select ui bug. Speed up
49 * multi_lag system. Put in new main hall.
51 * 13 4/18/98 5:00p Dave
52 * Put in observer zoom key. Made mission sync screen more informative.
54 * 12 4/06/98 12:37a Lawrance
55 * fix compile bug with demo
57 * 11 4/04/98 8:42p Dave
58 * Tested and debugged UDP reliable socket layer. Modified lag system to
59 * take this into account.
61 * 10 4/02/98 6:29p Lawrance
62 * compile out multilag code for demo
64 * 9 3/14/98 2:48p Dave
65 * Cleaned up observer joining code. Put in support for file xfers to
66 * ingame joiners (observers or not). Revamped and reinstalled pseudo
69 * 8 1/11/98 10:03p Allender
70 * removed <winsock.h> from headers which included it. Made psnet_socket
71 * type which is defined just as SOCKET type is.
73 * 7 12/29/97 5:21p Dave
74 * Put in object update sequencing for multiplayer.
76 * 6 12/16/97 6:17p Dave
77 * Put in primary weapon support for multiplayer weapon select screen.
79 * 5 12/10/97 4:45p Dave
80 * Added in more detailed support for multiplayer packet lag/loss. Fixed
81 * some multiplayer stuff. Added some controls to the standalone.
83 * 4 12/01/97 4:59p Dave
84 * Synchronized multiplayer debris objects. Put in pilot popup in main
85 * hall. Optimized simulated multiplayer lag module. Fixed a potential
88 * 3 11/28/97 7:04p Dave
89 * Emergency checkin due to big system crash.
91 * 2 11/28/97 5:06p Dave
92 * Put in facilities for simulating multiplayer lag.
94 * 1 11/28/97 4:38p Dave
104 #include <sys/time.h>
105 #include <sys/types.h>
106 #include <sys/socket.h>
107 #include <netinet/in.h>
108 #include <arpa/inet.h>
114 #include "multilag.h"
117 #include "linklist.h"
119 // ----------------------------------------------------------------------------------------------------
120 // LAGLOSS DEFINES/VARS
123 // default LAGLOSS values
124 #define MULTI_LAGLOSS_DEF_LAG (-1)
125 #define MULTI_LAGLOSS_DEF_LAGMIN (-1)
126 #define MULTI_LAGLOSS_DEF_LAGMAX (-1)
127 #define MULTI_LAGLOSS_DEF_LOSS (-1.0f)
128 #define MULTI_LAGLOSS_DEF_LOSSMIN (-1.0f)
129 #define MULTI_LAGLOSS_DEF_LOSSMAX (-1.0f)
130 #define MULTI_LAGLOSS_DEF_STREAK (2500)
133 int Multi_lag_inited = 0;
135 // lag values (base - max and min)
136 int Multi_lag_base = -1;
137 int Multi_lag_min = -1;
138 int Multi_lag_max = -1;
140 // packet loss values (base - max and min)
141 float Multi_loss_base = -1.0f;
142 float Multi_loss_min = -1.0f;
143 float Multi_loss_max = -1.0f;
145 // streaks for lagging
146 int Multi_streak_stamp = -1; // timestamp telling when the streak of a certain lag is done
147 int Multi_streak_time = 0; // how long each streak will last
148 int Multi_current_streak = -1; // what lag the current streak has
150 // struct for buffering stuff on receives
151 typedef struct lag_buf {
152 ubyte data[700]; // the data from the packet
153 int data_len; // length of the data
154 uint socket; // this can be either a PSNET_SOCKET or a PSNET_SOCKET_RELIABLE
155 int stamp; // when this expires, make this packet available
156 SOCKADDR_IN ip_addr; // ip address when in TCP
158 SOCKADDR_IPX ipx_addr; // ipx address when in IPX mode
161 struct lag_buf * prev; // prev in the list
162 struct lag_buf * next; // next in the list
165 // lag buffers - malloced
167 #define MAX_LAG_BUFFERS 1 // only 1 buffer in non-debug builds
169 #define MAX_LAG_BUFFERS 1000
171 lag_buf *Lag_buffers[MAX_LAG_BUFFERS];
172 int Lag_buf_count = 0; // how many lag_buf's are currently in use
174 lag_buf Lag_free_list;
175 lag_buf Lag_used_list;
178 // ----------------------------------------------------------------------------------------------------
179 // LAGLOSS FORWARD DECLARATIONS
182 // get a value to lag a packet with (in ms)
183 int multi_lag_get_random_lag();
185 // boolean yes or no - should this packet be lost?
186 int multi_lag_should_be_lost();
188 // get a free packet buffer, return NULL on fail
189 lag_buf *multi_lag_get_free();
191 // put a lag buffer back
192 void multi_lag_put_free(lag_buf *buf);
194 // ----------------------------------------------------------------------------------------------------
198 void multi_lag_init()
200 // never do lag in a non-debug build
201 #if defined(NDEBUG) || !defined(MULTI_USE_LAG)
202 Multi_lag_inited = 0;
206 // if we're already inited, don't do anything
207 if(Multi_lag_inited){
211 // try and allocate lag bufs
212 for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
213 Lag_buffers[idx] = (lag_buf*)malloc(sizeof(lag_buf));
214 if(Lag_buffers[idx] == NULL){
219 // initialize lag buffer lists
220 list_init( &Lag_free_list );
221 list_init( &Lag_used_list );
223 // Link all object slots into the free list
224 for (idx=0; idx<MAX_LAG_BUFFERS; idx++) {
225 list_append(&Lag_free_list, Lag_buffers[idx]);
228 // set the default lag values
229 Multi_lag_base = MULTI_LAGLOSS_DEF_LAG;
230 Multi_lag_min = MULTI_LAGLOSS_DEF_LAGMIN;
231 Multi_lag_max = MULTI_LAGLOSS_DEF_LAGMAX;
233 // set the default loss values
234 Multi_loss_base = MULTI_LAGLOSS_DEF_LOSS;
235 Multi_loss_min = MULTI_LAGLOSS_DEF_LOSSMIN;
236 Multi_loss_max = MULTI_LAGLOSS_DEF_LOSSMAX;
238 // set the default lag streak time
239 Multi_streak_time = MULTI_LAGLOSS_DEF_STREAK;
241 Multi_lag_inited = 1;
245 void multi_lag_close()
249 // if we're not inited already, don't do anything
250 if(!Multi_lag_inited){
254 // free up lag buffers
255 for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
256 if(Lag_buffers[idx] != NULL){
257 free(Lag_buffers[idx]);
258 Lag_buffers[idx] = NULL;
262 Multi_lag_inited = 0;
265 // select for multi_lag
266 int multi_lag_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *except_fds, timeval *timeout)
275 SOCKADDR_IPX ipx_addr;
278 lag_buf *moveup, *item;
280 Assert(readfds != NULL);
281 Assert(writefds == NULL);
282 Assert(except_fds == NULL);
284 // clear out addresses
285 memset(&ip_addr, 0, sizeof(SOCKADDR_IN));
287 memset(&ipx_addr, 0, sizeof(SOCKADDR_IPX));
290 // if there's data on the socket, read it
291 if(select(nfds, readfds, writefds, except_fds, timeout)){
292 // read the data and stuff it
294 t_from_len = sizeof(SOCKADDR_IN);
295 ret_val = recvfrom(readfds->fd_array[0], t_buf, 1024, 0, (SOCKADDR*)&ip_addr, &t_from_len);
298 t_from_len = sizeof(SOCKADDR_IPX);
299 ret_val = recvfrom(readfds->fd_array[0], t_buf, 1024, 0, (SOCKADDR*)&ipx_addr, &t_from_len);
303 // wacky socket error
304 if(ret_val == SOCKET_ERROR){
308 // if we should be dropping this packet
309 if(!multi_lag_should_be_lost()){
310 // get a free packet buf and stuff the data
311 item = multi_lag_get_free();
313 Assert(ret_val < 700);
314 memcpy(item->data, t_buf, ret_val);
315 item->data_len = ret_val;
316 item->ip_addr = ip_addr;
318 item->ipx_addr = ipx_addr;
320 item->socket = readfds->fd_array[0];
321 item->stamp = timestamp(multi_lag_get_random_lag());
326 // always unset the readfds
327 readfds->fd_count = 0;
329 // now determine if we have any pending packets - find the first one
330 // NOTE : this _could_ be the packet we just read. In fact, with a 0 lag, this will always be the case
331 moveup=GET_FIRST(&Lag_used_list);
332 while ( moveup!=END_OF_LIST(&Lag_used_list) ) {
333 // if the timestamp has elapsed and we have a matching socket
334 if((readfds->fd_array[0] == (SOCKET)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
335 // set this so we think select returned yes
336 readfds->fd_count = 1;
340 moveup = GET_NEXT(moveup);
348 // recvfrom for multilag
349 int multi_lag_recvfrom(uint s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
351 lag_buf *moveup = NULL;
352 lag_buf *item = NULL;
354 // now determine if we have any pending packets - find the first one
355 moveup=GET_FIRST(&Lag_used_list);
356 while ( moveup!=END_OF_LIST(&Lag_used_list) ) {
357 // if the timestamp has elapsed
358 if((s == (SOCKET)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
363 moveup = GET_NEXT(moveup);
366 // if this happens, it means that the multi_lag_select() returned an improper value
369 Assert(item->data_len <= len);
370 memcpy(buf, item->data, item->data_len);
372 memcpy(from, &item->ip_addr, sizeof(SOCKADDR_IN));
375 memcpy(from, &item->ipx_addr, sizeof(SOCKADDR_IPX));
379 // stick the item back on the free list
380 multi_lag_put_free(item);
382 // return the size in bytes
383 return item->data_len;
386 // ----------------------------------------------------------------------------------------------------
387 // LAGLOSS FORWARD DEFINITIONS
390 int multi_lag_get_random_lag()
392 // first determine the percentage we'll be checking against
396 // if the lag system isn't inited, don't do anything (no lag)
397 if(!Multi_lag_inited){
402 // see if we should be going up or down (loss max/loss min)
404 if((float)rand()/(float)RAND_MAX < 0.5){
406 if(Multi_lag_min >= 0){
407 mod = - (int)((float)(Multi_lag_base - Multi_lag_min) * ((float)rand()/(float)RAND_MAX));
411 if(Multi_lag_max >= 0){
412 mod = (int)((float)(Multi_lag_max - Multi_lag_base) * ((float)rand()/(float)RAND_MAX));
416 // if the current streak has elapsed, calculate a new one
417 if((Multi_streak_stamp == -1) || (timestamp_elapsed(Multi_streak_stamp))){
418 // timestamp the new streak
419 Multi_streak_stamp = timestamp(Multi_streak_time);
421 // set the return value
422 ret = Multi_lag_base + mod;
424 // set the lag value of this current streak
425 Multi_current_streak = ret;
427 // otherwise use the lag for the current streak
429 ret = Multi_current_streak;
435 // this _may_ be a bit heavyweight, but it _is_ debug code
436 int multi_lag_should_be_lost()
438 // first determine the percentage we'll be checking against
441 // if the lag system isn't inited, don't do anything
442 if(!Multi_lag_inited){
446 // see if we should be going up or down (loss max/loss min)
448 if((float)rand()/(float)RAND_MAX < 0.5){
450 if(Multi_loss_min >= 0.0f){
451 mod = - ((Multi_loss_base - Multi_loss_min) * ((float)rand()/(float)RAND_MAX));
455 if(Multi_loss_max >= 0.0f){
456 mod = ((Multi_loss_max - Multi_loss_base) * ((float)rand()/(float)RAND_MAX));
460 if((float)rand()/(float)RAND_MAX <= Multi_loss_base + mod){
467 // get a free packet buffer, return NULL on fail
468 lag_buf *multi_lag_get_free()
472 // if we're out of buffers
473 if(Lag_buf_count >= MAX_LAG_BUFFERS){
474 nprintf(("Network", "Out of lag buffers!\n"));
479 lagp = GET_FIRST(&Lag_free_list);
480 Assert( lagp != &Lag_free_list ); // shouldn't have the dummy element
482 // remove trailp from the free list
483 list_remove( &Lag_free_list, lagp );
485 // insert trailp onto the end of used list
486 list_append( &Lag_used_list, lagp );
488 // increase the count
493 // put a lag buffer back
494 void multi_lag_put_free(lag_buf *buf)
496 // remove objp from the used list
497 list_remove( &Lag_used_list, buf);
499 // add objp to the end of the free
500 list_append( &Lag_free_list, buf );
506 void multi_lagloss_dcf()
508 // if the lag system isn't inited, don't do anything
509 if(!Multi_lag_inited){
510 dc_printf("Lag System Not Initialized!\n");
514 // display all available commands
515 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");
517 // display lag settings
519 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);
521 // display loss settings
522 dc_printf("Loss : ");
523 dc_printf("\n Base %f\n Min %f\n Max %f\n", Multi_loss_base, Multi_loss_min, Multi_loss_max);
528 // if the lag system isn't inited, don't do anything
529 if(!Multi_lag_inited){
530 dc_printf("Lag System Not Initialized!\n");
535 // parse the argument and change things around accordingly
536 if(Dc_arg_type & ARG_INT){
538 // switch the lag sim off
542 dc_printf("Turning simulated lag off\n");
544 } else if((Multi_lag_max >= 0) && (Dc_arg_int > Multi_lag_max)){
545 dc_printf("Base value greater than max value, ignoring...");
546 } else if((Multi_lag_min >= 0) && (Dc_arg_int < Multi_lag_min)){
547 dc_printf("Base value smaller than min value, ignoring...");
549 Multi_lag_base = Dc_arg_int;
557 // if the lag system isn't inited, don't do anything
558 if(!Multi_lag_inited){
559 dc_printf("Lag System Not Initialized!\n");
564 // parse the argument and change things around accordingly
565 if(Dc_arg_type & ARG_INT){
566 if(Dc_arg_int > Multi_lag_base){
567 dc_printf("Min value greater than base value, ignoring...");
572 Multi_lag_min = Dc_arg_int;
581 // if the lag system isn't inited, don't do anything
582 if(!Multi_lag_inited){
583 dc_printf("Lag System Not Initialized!\n");
587 // parse the argument and change things around accordingly
589 if(Dc_arg_type & ARG_INT){
590 if((Dc_arg >=0) && (Dc_arg_int < Multi_lag_base)){
591 dc_printf("Max value smaller than base value, ignoring...");
596 Multi_lag_max = Dc_arg_int;
605 // if the lag system isn't inited, don't do anything
606 if(!Multi_lag_inited){
607 dc_printf("Lag System Not Initialized!\n");
611 // parse the argument and change things around accordingly
613 if(Dc_arg_type & ARG_INT){
614 float val = (float)Dc_arg_int / 100.0f;
616 if(Dc_arg_int > 100){
617 dc_printf("Illegal loss base value, ignoring...");
618 } else if(Dc_arg_int < 0){
619 // switch the loss sim off
620 dc_printf("Turning simulated loss off\n");
621 Multi_loss_base = -1.0f;
622 Multi_loss_min = -1.0f;
623 Multi_loss_max = -1.0f;
625 } else if((Multi_loss_max >= 0.0f) && (val > Multi_loss_max)){
626 dc_printf("Base value greater than max value, ignoring...");
627 } else if((Multi_loss_min >= 0.0f) && (val < Multi_loss_min)){
628 dc_printf("Base value smaller than min value, ignoring...");
630 Multi_loss_base = val;
638 // if the lag system isn't inited, don't do anything
639 if(!Multi_lag_inited){
640 dc_printf("Lag System Not Initialized!\n");
644 // parse the argument and change things around accordingly
646 if(Dc_arg_type & ARG_INT){
647 float val = (float)Dc_arg_int / 100.0f;
649 if(val > Multi_loss_base){
650 dc_printf("Min value greater than base value, ignoring...");
652 // otherwise set the value
654 Multi_loss_min = -1.0f;
656 Multi_loss_min = val;
665 // if the lag system isn't inited, don't do anything
666 if(!Multi_lag_inited){
667 dc_printf("Lag System Not Initialized!\n");
671 // parse the argument and change things around accordingly
673 if(Dc_arg_type & ARG_INT){
674 float val = (float)Dc_arg_int / 100.0f;
676 if(val < Multi_loss_base){
677 dc_printf("Max value smaller than base value, ignoring...");
679 // otherwise set the value
681 Multi_loss_max = -1.0f;
683 Multi_loss_min = val;
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");
703 // if the lag system isn't inited, don't do anything
704 if(!Multi_lag_inited){
705 dc_printf("Lag System Not Initialized!\n");
710 if(Dc_arg_type & ARG_INT){
712 Multi_streak_time = Dc_arg_int;
719 // if the lag system isn't inited, don't do anything
720 if(!Multi_lag_inited){
721 dc_printf("Lag System Not Initialized!\n");
725 dc_printf("Setting bad lag/loss parameters\n");
727 // set good lagloss parameters
728 Multi_lag_base = 500;
732 Multi_loss_base = 0.2f;
733 Multi_loss_min = 0.15f;
734 Multi_loss_max = 0.23f;
736 Multi_streak_time = 800;
737 Multi_streak_stamp = -1;
738 Multi_current_streak = -1;
743 // if the lag system isn't inited, don't do anything
744 if(!Multi_lag_inited){
745 dc_printf("Lag System Not Initialized!\n");
749 dc_printf("Setting avg lag/loss parameters\n");
751 // set good lagloss parameters
752 Multi_lag_base = 275;
756 Multi_loss_base = 0.15f;
757 Multi_loss_min = 0.1f;
758 Multi_loss_max = 0.20f;
760 Multi_streak_time = 900;
761 Multi_streak_stamp = -1;
762 Multi_current_streak = -1;
767 // if the lag system isn't inited, don't do anything
768 if(!Multi_lag_inited){
769 dc_printf("Lag System Not Initialized!\n");
773 dc_printf("Setting good lag/loss parameters\n");
775 // set good lagloss parameters
776 Multi_lag_base = 100;
780 Multi_loss_base = 0.08f;
781 Multi_loss_min = 0.0f;
782 Multi_loss_max = 0.1f;
784 Multi_streak_time = 1000;
785 Multi_streak_stamp = -1;
786 Multi_current_streak = -1;