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.6 2004/07/04 11:39:06 taylor
17 * fix missing debrief text, crash on exit, path separator's, warning fixes, no GR_SOFT
19 * Revision 1.5 2002/06/09 04:41:24 relnev
20 * added copyright header
22 * Revision 1.4 2002/05/26 20:49:54 theoddone33
25 * Revision 1.3 2002/05/26 20:22:48 theoddone33
26 * Most of network/ works
28 * Revision 1.2 2002/05/07 03:16:47 theoddone33
29 * The Great Newline Fix
31 * Revision 1.1.1.1 2002/05/03 03:28:10 root
35 * 4 11/19/98 8:03a Dave
36 * Full support for D3-style reliable sockets. Revamped packet lag/loss
37 * system, made it receiver side and at the lowest possible level.
39 * 3 11/17/98 11:12a Dave
40 * Removed player identification by address. Now assign explicit id #'s.
42 * 2 10/07/98 10:53a Dave
45 * 1 10/07/98 10:50a Dave
47 * 15 6/05/98 9:54a Lawrance
50 * 14 4/27/98 6:02p Dave
51 * Modify how missile scoring works. Fixed a team select ui bug. Speed up
52 * multi_lag system. Put in new main hall.
54 * 13 4/18/98 5:00p Dave
55 * Put in observer zoom key. Made mission sync screen more informative.
57 * 12 4/06/98 12:37a Lawrance
58 * fix compile bug with demo
60 * 11 4/04/98 8:42p Dave
61 * Tested and debugged UDP reliable socket layer. Modified lag system to
62 * take this into account.
64 * 10 4/02/98 6:29p Lawrance
65 * compile out multilag code for demo
67 * 9 3/14/98 2:48p Dave
68 * Cleaned up observer joining code. Put in support for file xfers to
69 * ingame joiners (observers or not). Revamped and reinstalled pseudo
72 * 8 1/11/98 10:03p Allender
73 * removed <winsock.h> from headers which included it. Made psnet_socket
74 * type which is defined just as SOCKET type is.
76 * 7 12/29/97 5:21p Dave
77 * Put in object update sequencing for multiplayer.
79 * 6 12/16/97 6:17p Dave
80 * Put in primary weapon support for multiplayer weapon select screen.
82 * 5 12/10/97 4:45p Dave
83 * Added in more detailed support for multiplayer packet lag/loss. Fixed
84 * some multiplayer stuff. Added some controls to the standalone.
86 * 4 12/01/97 4:59p Dave
87 * Synchronized multiplayer debris objects. Put in pilot popup in main
88 * hall. Optimized simulated multiplayer lag module. Fixed a potential
91 * 3 11/28/97 7:04p Dave
92 * Emergency checkin due to big system crash.
94 * 2 11/28/97 5:06p Dave
95 * Put in facilities for simulating multiplayer lag.
97 * 1 11/28/97 4:38p Dave
104 #include <winsock2.h>
106 #include <sys/time.h>
107 #include <sys/types.h>
108 #include <sys/socket.h>
109 #include <netinet/in.h>
110 #include <arpa/inet.h>
116 #include "multilag.h"
119 #include "linklist.h"
121 // ----------------------------------------------------------------------------------------------------
122 // LAGLOSS DEFINES/VARS
125 // default LAGLOSS values
126 #define MULTI_LAGLOSS_DEF_LAG (-1)
127 #define MULTI_LAGLOSS_DEF_LAGMIN (-1)
128 #define MULTI_LAGLOSS_DEF_LAGMAX (-1)
129 #define MULTI_LAGLOSS_DEF_LOSS (-1.0f)
130 #define MULTI_LAGLOSS_DEF_LOSSMIN (-1.0f)
131 #define MULTI_LAGLOSS_DEF_LOSSMAX (-1.0f)
132 #define MULTI_LAGLOSS_DEF_STREAK (2500)
135 int Multi_lag_inited = 0;
137 // lag values (base - max and min)
138 int Multi_lag_base = -1;
139 int Multi_lag_min = -1;
140 int Multi_lag_max = -1;
142 // packet loss values (base - max and min)
143 float Multi_loss_base = -1.0f;
144 float Multi_loss_min = -1.0f;
145 float Multi_loss_max = -1.0f;
147 // streaks for lagging
148 int Multi_streak_stamp = -1; // timestamp telling when the streak of a certain lag is done
149 int Multi_streak_time = 0; // how long each streak will last
150 int Multi_current_streak = -1; // what lag the current streak has
152 // struct for buffering stuff on receives
153 typedef struct lag_buf {
154 ubyte data[700]; // the data from the packet
155 int data_len; // length of the data
156 uint socket; // this can be either a PSNET_SOCKET or a PSNET_SOCKET_RELIABLE
157 int stamp; // when this expires, make this packet available
158 struct sockaddr_in ip_addr; // ip address when in TCP
160 struct lag_buf * prev; // prev in the list
161 struct lag_buf * next; // next in the list
164 // lag buffers - malloced
166 #define MAX_LAG_BUFFERS 1 // only 1 buffer in non-debug builds
168 #define MAX_LAG_BUFFERS 1000
170 lag_buf *Lag_buffers[MAX_LAG_BUFFERS];
171 int Lag_buf_count = 0; // how many lag_buf's are currently in use
173 lag_buf Lag_free_list;
174 lag_buf Lag_used_list;
177 // ----------------------------------------------------------------------------------------------------
178 // LAGLOSS FORWARD DECLARATIONS
181 // get a value to lag a packet with (in ms)
182 int multi_lag_get_random_lag();
184 // boolean yes or no - should this packet be lost?
185 int multi_lag_should_be_lost();
187 // get a free packet buffer, return NULL on fail
188 lag_buf *multi_lag_get_free();
190 // put a lag buffer back
191 void multi_lag_put_free(lag_buf *buf);
193 // ----------------------------------------------------------------------------------------------------
197 void multi_lag_init()
199 // never do lag in a non-debug build
200 #if defined(NDEBUG) || !defined(MULTI_USE_LAG)
201 Multi_lag_inited = 0;
205 // if we're already inited, don't do anything
206 if(Multi_lag_inited){
210 // try and allocate lag bufs
211 for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
212 Lag_buffers[idx] = (lag_buf*)malloc(sizeof(lag_buf));
213 if(Lag_buffers[idx] == NULL){
218 // initialize lag buffer lists
219 list_init( &Lag_free_list );
220 list_init( &Lag_used_list );
222 // Link all object slots into the free list
223 for (idx=0; idx<MAX_LAG_BUFFERS; idx++) {
224 list_append(&Lag_free_list, Lag_buffers[idx]);
227 // set the default lag values
228 Multi_lag_base = MULTI_LAGLOSS_DEF_LAG;
229 Multi_lag_min = MULTI_LAGLOSS_DEF_LAGMIN;
230 Multi_lag_max = MULTI_LAGLOSS_DEF_LAGMAX;
232 // set the default loss values
233 Multi_loss_base = MULTI_LAGLOSS_DEF_LOSS;
234 Multi_loss_min = MULTI_LAGLOSS_DEF_LOSSMIN;
235 Multi_loss_max = MULTI_LAGLOSS_DEF_LOSSMAX;
237 // set the default lag streak time
238 Multi_streak_time = MULTI_LAGLOSS_DEF_STREAK;
240 Multi_lag_inited = 1;
244 void multi_lag_close()
248 // if we're not inited already, don't do anything
249 if(!Multi_lag_inited){
253 // free up lag buffers
254 for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
255 if(Lag_buffers[idx] != NULL){
256 free(Lag_buffers[idx]);
257 Lag_buffers[idx] = NULL;
261 Multi_lag_inited = 0;
264 // select for multi_lag
265 int multi_lag_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *except_fds, timeval *timeout)
268 SOCKLEN_T t_from_len;
269 struct sockaddr_in ip_addr;
270 int ret_val = SOCKET_ERROR;
271 lag_buf *moveup, *item;
273 SDL_assert(readfds != NULL);
274 SDL_assert(writefds == NULL);
275 SDL_assert(except_fds == NULL);
277 // clear out addresses
278 memset(&ip_addr, 0, sizeof(struct sockaddr_in));
280 // if there's data on the socket, read it
281 if(select(nfds+1, readfds, writefds, except_fds, timeout)){
282 // read the data and stuff it
284 t_from_len = sizeof(struct sockaddr_in);
285 ret_val = recvfrom(nfds, t_buf, 1024, 0, (struct sockaddr*)&ip_addr, &t_from_len);
291 // wacky socket error
292 if(ret_val == SOCKET_ERROR){
296 // if we should be dropping this packet
297 if(!multi_lag_should_be_lost()){
298 // get a free packet buf and stuff the data
299 item = multi_lag_get_free();
301 SDL_assert(ret_val < 700);
302 memcpy(item->data, t_buf, ret_val);
303 item->data_len = ret_val;
304 item->ip_addr = ip_addr;
306 item->stamp = timestamp(multi_lag_get_random_lag());
311 // always unset the readfds
312 FD_CLR((SOCKET)nfds, readfds);
314 // now determine if we have any pending packets - find the first one
315 // NOTE : this _could_ be the packet we just read. In fact, with a 0 lag, this will always be the case
316 moveup=GET_FIRST(&Lag_used_list);
317 while ( moveup!=END_OF_LIST(&Lag_used_list) ) {
318 // if the timestamp has elapsed and we have a matching socket
319 if((nfds == (int)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
320 // set this so we think select returned yes
321 FD_SET((SOCKET)nfds, readfds);
325 moveup = GET_NEXT(moveup);
332 // recvfrom for multilag
333 int multi_lag_recvfrom(uint s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen)
335 lag_buf *moveup = NULL;
336 lag_buf *item = NULL;
338 // now determine if we have any pending packets - find the first one
339 moveup=GET_FIRST(&Lag_used_list);
340 while ( moveup!=END_OF_LIST(&Lag_used_list) ) {
341 // if the timestamp has elapsed
342 if(((int)s == (SOCKET)moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
347 moveup = GET_NEXT(moveup);
350 // if this happens, it means that the multi_lag_select() returned an improper value
357 SDL_assert(item->data_len <= len);
358 memcpy(buf, item->data, item->data_len);
360 memcpy(from, &item->ip_addr, sizeof(struct sockaddr_in));
365 // stick the item back on the free list
366 multi_lag_put_free(item);
368 // return the size in bytes
369 return item->data_len;
372 // ----------------------------------------------------------------------------------------------------
373 // LAGLOSS FORWARD DEFINITIONS
376 int multi_lag_get_random_lag()
378 // first determine the percentage we'll be checking against
382 // if the lag system isn't inited, don't do anything (no lag)
383 if(!Multi_lag_inited){
388 // see if we should be going up or down (loss max/loss min)
390 if((float)myrand()/(float)MY_RAND_MAX < 0.5){
392 if(Multi_lag_min >= 0){
393 mod = - (int)((float)(Multi_lag_base - Multi_lag_min) * ((float)myrand()/(float)MY_RAND_MAX));
397 if(Multi_lag_max >= 0){
398 mod = (int)((float)(Multi_lag_max - Multi_lag_base) * ((float)myrand()/(float)MY_RAND_MAX));
402 // if the current streak has elapsed, calculate a new one
403 if((Multi_streak_stamp == -1) || (timestamp_elapsed(Multi_streak_stamp))){
404 // timestamp the new streak
405 Multi_streak_stamp = timestamp(Multi_streak_time);
407 // set the return value
408 ret = Multi_lag_base + mod;
410 // set the lag value of this current streak
411 Multi_current_streak = ret;
413 // otherwise use the lag for the current streak
415 ret = Multi_current_streak;
421 // this _may_ be a bit heavyweight, but it _is_ debug code
422 int multi_lag_should_be_lost()
424 // first determine the percentage we'll be checking against
427 // if the lag system isn't inited, don't do anything
428 if(!Multi_lag_inited){
432 // see if we should be going up or down (loss max/loss min)
434 if((float)myrand()/(float)MY_RAND_MAX < 0.5){
436 if(Multi_loss_min >= 0.0f){
437 mod = - ((Multi_loss_base - Multi_loss_min) * ((float)myrand()/(float)MY_RAND_MAX));
441 if(Multi_loss_max >= 0.0f){
442 mod = ((Multi_loss_max - Multi_loss_base) * ((float)myrand()/(float)MY_RAND_MAX));
446 if((float)myrand()/(float)MY_RAND_MAX <= Multi_loss_base + mod){
453 // get a free packet buffer, return NULL on fail
454 lag_buf *multi_lag_get_free()
458 // if we're out of buffers
459 if(Lag_buf_count >= MAX_LAG_BUFFERS){
460 nprintf(("Network", "Out of lag buffers!\n"));
465 lagp = GET_FIRST(&Lag_free_list);
466 SDL_assert( lagp != &Lag_free_list ); // shouldn't have the dummy element
468 // remove trailp from the free list
469 list_remove( &Lag_free_list, lagp );
471 // insert trailp onto the end of used list
472 list_append( &Lag_used_list, lagp );
474 // increase the count
479 // put a lag buffer back
480 void multi_lag_put_free(lag_buf *buf)
482 // remove objp from the used list
483 list_remove( &Lag_used_list, buf);
485 // add objp to the end of the free
486 list_append( &Lag_free_list, buf );
492 void multi_lagloss_dcf()
494 // if the lag system isn't inited, don't do anything
495 if(!Multi_lag_inited){
496 dc_printf("Lag System Not Initialized!\n");
500 // display all available commands
501 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");
503 // display lag settings
505 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);
507 // display loss settings
508 dc_printf("Loss : ");
509 dc_printf("\n Base %f\n Min %f\n Max %f\n", Multi_loss_base, Multi_loss_min, Multi_loss_max);
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){
524 // switch the lag sim off
528 dc_printf("Turning simulated lag off\n");
530 } else if((Multi_lag_max >= 0) && (Dc_arg_int > Multi_lag_max)){
531 dc_printf("Base value greater than max value, ignoring...");
532 } else if((Multi_lag_min >= 0) && (Dc_arg_int < Multi_lag_min)){
533 dc_printf("Base value smaller than min value, ignoring...");
535 Multi_lag_base = Dc_arg_int;
543 // if the lag system isn't inited, don't do anything
544 if(!Multi_lag_inited){
545 dc_printf("Lag System Not Initialized!\n");
550 // parse the argument and change things around accordingly
551 if(Dc_arg_type & ARG_INT){
552 if(Dc_arg_int > Multi_lag_base){
553 dc_printf("Min value greater than base value, ignoring...");
558 Multi_lag_min = Dc_arg_int;
567 // if the lag system isn't inited, don't do anything
568 if(!Multi_lag_inited){
569 dc_printf("Lag System Not Initialized!\n");
573 // parse the argument and change things around accordingly
575 if(Dc_arg_type & ARG_INT){
576 if((Dc_arg_int >=0) && (Dc_arg_int < Multi_lag_base)){
577 dc_printf("Max value smaller than base value, ignoring...");
582 Multi_lag_max = Dc_arg_int;
591 // if the lag system isn't inited, don't do anything
592 if(!Multi_lag_inited){
593 dc_printf("Lag System Not Initialized!\n");
597 // parse the argument and change things around accordingly
599 if(Dc_arg_type & ARG_INT){
600 float val = (float)Dc_arg_int / 100.0f;
602 if(Dc_arg_int > 100){
603 dc_printf("Illegal loss base value, ignoring...");
604 } else if(Dc_arg_int < 0){
605 // switch the loss sim off
606 dc_printf("Turning simulated loss off\n");
607 Multi_loss_base = -1.0f;
608 Multi_loss_min = -1.0f;
609 Multi_loss_max = -1.0f;
611 } else if((Multi_loss_max >= 0.0f) && (val > Multi_loss_max)){
612 dc_printf("Base value greater than max value, ignoring...");
613 } else if((Multi_loss_min >= 0.0f) && (val < Multi_loss_min)){
614 dc_printf("Base value smaller than min value, ignoring...");
616 Multi_loss_base = val;
624 // if the lag system isn't inited, don't do anything
625 if(!Multi_lag_inited){
626 dc_printf("Lag System Not Initialized!\n");
630 // parse the argument and change things around accordingly
632 if(Dc_arg_type & ARG_INT){
633 float val = (float)Dc_arg_int / 100.0f;
635 if(val > Multi_loss_base){
636 dc_printf("Min value greater than base value, ignoring...");
638 // otherwise set the value
640 Multi_loss_min = -1.0f;
642 Multi_loss_min = val;
651 // if the lag system isn't inited, don't do anything
652 if(!Multi_lag_inited){
653 dc_printf("Lag System Not Initialized!\n");
657 // parse the argument and change things around accordingly
659 if(Dc_arg_type & ARG_INT){
660 float val = (float)Dc_arg_int / 100.0f;
662 if(val < Multi_loss_base){
663 dc_printf("Max value smaller than base value, ignoring...");
665 // otherwise set the value
667 Multi_loss_max = -1.0f;
669 Multi_loss_min = val;
678 // if the lag system isn't inited, don't do anything
679 if(!Multi_lag_inited){
680 dc_printf("Lag System Not Initialized!\n");
689 // if the lag system isn't inited, don't do anything
690 if(!Multi_lag_inited){
691 dc_printf("Lag System Not Initialized!\n");
696 if(Dc_arg_type & ARG_INT){
698 Multi_streak_time = Dc_arg_int;
705 // if the lag system isn't inited, don't do anything
706 if(!Multi_lag_inited){
707 dc_printf("Lag System Not Initialized!\n");
711 dc_printf("Setting bad lag/loss parameters\n");
713 // set good lagloss parameters
714 Multi_lag_base = 500;
718 Multi_loss_base = 0.2f;
719 Multi_loss_min = 0.15f;
720 Multi_loss_max = 0.23f;
722 Multi_streak_time = 800;
723 Multi_streak_stamp = -1;
724 Multi_current_streak = -1;
729 // if the lag system isn't inited, don't do anything
730 if(!Multi_lag_inited){
731 dc_printf("Lag System Not Initialized!\n");
735 dc_printf("Setting avg lag/loss parameters\n");
737 // set good lagloss parameters
738 Multi_lag_base = 275;
742 Multi_loss_base = 0.15f;
743 Multi_loss_min = 0.1f;
744 Multi_loss_max = 0.20f;
746 Multi_streak_time = 900;
747 Multi_streak_stamp = -1;
748 Multi_current_streak = -1;
753 // if the lag system isn't inited, don't do anything
754 if(!Multi_lag_inited){
755 dc_printf("Lag System Not Initialized!\n");
759 dc_printf("Setting good lag/loss parameters\n");
761 // set good lagloss parameters
762 Multi_lag_base = 100;
766 Multi_loss_base = 0.08f;
767 Multi_loss_min = 0.0f;
768 Multi_loss_max = 0.1f;
770 Multi_streak_time = 1000;
771 Multi_streak_stamp = -1;
772 Multi_current_streak = -1;