/*
 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
 *
 * All source code herein is the property of Volition, Inc. You may not sell 
 * or otherwise commercially exploit the source or things you created based on the 
 * source.
 *
*/


#ifdef _WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#endif

#include "network/multilag.h"
#include "io/timer.h"
#include "globalincs/linklist.h"
#include "network/psnet2.h"
#include "debugconsole/console.h"


// ----------------------------------------------------------------------------------------------------
// LAGLOSS DEFINES/VARS
//

// default LAGLOSS values
#define MULTI_LAGLOSS_DEF_LAG				(-1)
#define MULTI_LAGLOSS_DEF_LAGMIN			(-1)
#define MULTI_LAGLOSS_DEF_LAGMAX			(-1)
#define MULTI_LAGLOSS_DEF_LOSS			(-1.0f)
#define MULTI_LAGLOSS_DEF_LOSSMIN		(-1.0f)
#define MULTI_LAGLOSS_DEF_LOSSMAX		(-1.0f)
#define MULTI_LAGLOSS_DEF_STREAK			(2500)

// if we're running
int Multi_lag_inited = 0;

// lag values (base - max and min)
int Multi_lag_base = -1;
int Multi_lag_min = -1;
int Multi_lag_max = -1;

// packet loss values (base - max and min)
float Multi_loss_base = -1.0f;
float Multi_loss_min = -1.0f;
float Multi_loss_max = -1.0f;

// streaks for lagging
int Multi_streak_stamp = -1;				// timestamp telling when the streak of a certain lag is done
int Multi_streak_time = 0;					// how long each streak will last
int Multi_current_streak = -1;			// what lag the current streak has

// struct for buffering stuff on receives
typedef struct lag_buf {
	SOCKADDR_STORAGE ip_addr;						// ip address
	SSIZE_T data_len;								// length of the data
	ubyte data[700];							// the data from the packet
	SOCKET socket;								// this can be either a PSNET_SOCKET or a PSNET_SOCKET_RELIABLE
	int stamp;									// when this expires, make this packet available	

	uint8_t __pad[4];	// alignment

	struct	lag_buf * prev;				// prev in the list
	struct	lag_buf * next;				// next in the list
} lag_buf;

// lag buffers - malloced
#ifdef NDEBUG
	#define MAX_LAG_BUFFERS			1		// only 1 buffer in non-debug builds
#else
	#define MAX_LAG_BUFFERS			1000
#endif
lag_buf *Lag_buffers[MAX_LAG_BUFFERS];
int Lag_buf_count = 0;						// how many lag_buf's are currently in use

lag_buf Lag_free_list;
lag_buf Lag_used_list;


// ----------------------------------------------------------------------------------------------------
// LAGLOSS FORWARD DECLARATIONS
//

// get a value to lag a packet with (in ms)
int multi_lag_get_random_lag();

// boolean yes or no - should this packet be lost?
int multi_lag_should_be_lost();		    

// get a free packet buffer, return NULL on fail
lag_buf *multi_lag_get_free();

// put a lag buffer back
void multi_lag_put_free(lag_buf *buf);

// ----------------------------------------------------------------------------------------------------
// LAGLOSS FUNCTIONS
//

void multi_lag_init()
{	
	// never do lag in a non-debug build
#if defined(NDEBUG) || !defined(MULTI_USE_LAG)
	Multi_lag_inited = 0;
#else
	int idx;

	// if we're already inited, don't do anything
	if(Multi_lag_inited){
		return;
	}

	// try and allocate lag bufs
	for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
		Lag_buffers[idx] = (lag_buf*)vm_malloc(sizeof(lag_buf));
		if(Lag_buffers[idx] == NULL){
			return;
		}
	}

	// initialize lag buffer lists
	list_init( &Lag_free_list );
	list_init( &Lag_used_list );

	// Link all object slots into the free list
	for (idx=0; idx<MAX_LAG_BUFFERS; idx++)	{
		list_append(&Lag_free_list, Lag_buffers[idx]);
	}
	
	// set the default lag values
	Multi_lag_base = MULTI_LAGLOSS_DEF_LAG;
	Multi_lag_min = MULTI_LAGLOSS_DEF_LAGMIN;
	Multi_lag_max = MULTI_LAGLOSS_DEF_LAGMAX;

	// set the default loss values
	Multi_loss_base = MULTI_LAGLOSS_DEF_LOSS;
	Multi_loss_min	= MULTI_LAGLOSS_DEF_LOSSMIN;
	Multi_loss_max = MULTI_LAGLOSS_DEF_LOSSMAX;

	// set the default lag streak time	
	Multi_streak_time = MULTI_LAGLOSS_DEF_STREAK;
	
	Multi_lag_inited = 1;
#endif
}

void multi_lag_close()
{	
	int idx;

	// if we're not inited already, don't do anything
	if(!Multi_lag_inited){
		return;
	}	

	// free up lag buffers
	for(idx=0; idx<MAX_LAG_BUFFERS; idx++){
		if(Lag_buffers[idx] != NULL){
			vm_free(Lag_buffers[idx]);
			Lag_buffers[idx] = NULL;
		}
	}

	Multi_lag_inited = 0;
}

// select for multi_lag
int multi_lag_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *except_fds, timeval *timeout)
{
	#define T_BUF_SIZE	1024

	char t_buf[T_BUF_SIZE];
	socklen_t t_from_len;
	SOCKADDR_STORAGE ip_addr;
	SSIZE_T ret_val;
	lag_buf *moveup, *item;
	SOCKET sckt = 0;

	Assert(readfds != NULL);
	Assert(writefds == NULL);
	Assert(except_fds == NULL);

	// clear out addresses
	memset(&ip_addr, 0, sizeof(ip_addr));

#ifdef _WIN32
	sckt = readfds->fd_array[0];
#else
	sckt = nfds - 1;
#endif

	// if there's data on the socket, read it
	if ( select(nfds, readfds, writefds, except_fds, timeout) != SOCKET_ERROR ) {
		if ( !FD_ISSET(sckt, readfds) ) {
			return 0;
		}

		// read the data and stuff it
		t_from_len = sizeof(ip_addr);
		ret_val = recvfrom(sckt, t_buf, T_BUF_SIZE, 0, reinterpret_cast<LPSOCKADDR>(&ip_addr), &t_from_len);
			
		// wacky socket error
		if(ret_val == SOCKET_ERROR){
			return SOCKET_ERROR;
		}

		// if we should be dropping this packet
		if(!multi_lag_should_be_lost()){
			// get a free packet buf and stuff the data
			item = multi_lag_get_free();
			if(item){
				Assert(ret_val < 700);
				memcpy(item->data, t_buf, static_cast<size_t>(ret_val));
				item->data_len = ret_val;
				item->ip_addr = ip_addr;
				item->socket = sckt;
				item->stamp = timestamp(multi_lag_get_random_lag());
			}		
		}
	}

	// always unset the readfds
	FD_CLR(sckt, readfds);

	// now determine if we have any pending packets - find the first one
	// NOTE : this _could_ be the packet we just read. In fact, with a 0 lag, this will always be the case
	moveup=GET_FIRST(&Lag_used_list);
	while ( moveup!=END_OF_LIST(&Lag_used_list) )	{		
		// if the timestamp has elapsed and we have a matching socket
		if ( (sckt == moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp)) ) {
			// set this so we think select returned yes
			FD_SET(sckt, readfds);
			return 1;
		}

		moveup = GET_NEXT(moveup);
	}

	// no data
	return 0;
}

// recvfrom for multilag
int multi_lag_recvfrom(SOCKET s, char *buf, int len, int /*flags*/, struct sockaddr *from, int *fromlen)
{
	lag_buf *moveup = NULL;
	lag_buf *item = NULL;

	// now determine if we have any pending packets - find the first one
	moveup=GET_FIRST(&Lag_used_list);
	while ( moveup!=END_OF_LIST(&Lag_used_list) )	{		
		// if the timestamp has elapsed
		if((s == moveup->socket) && ((moveup->stamp <= 0) || timestamp_elapsed(moveup->stamp))){
			item = moveup;
			break;
		}

		moveup = GET_NEXT(moveup);
	}

	// if this happens, it means that the multi_lag_select() returned an improper value
	Assert(item);
	// stuff the data
	Assert(item->data_len <= len);
	memcpy(buf, item->data, static_cast<size_t>(item->data_len));
	Assertion(psnet_is_active(), "multi_lag_recvfrom(): TCP/IP is not active!");

	switch (item->ip_addr.ss_family) {
		case AF_INET: {
			Assert(*fromlen >= static_cast<int>(sizeof(SOCKADDR_IN)));

			memcpy(from, &item->ip_addr, sizeof(SOCKADDR_IN));
			*fromlen = sizeof(SOCKADDR_IN);

			break;
		}

		case AF_INET6: {
			Assert(*fromlen >= static_cast<int>(sizeof(SOCKADDR_IN6)));

			memcpy(from, &item->ip_addr, sizeof(SOCKADDR_IN6));
			*fromlen = sizeof(SOCKADDR_IN6);

			break;
		}

		default: {
			Assert(*fromlen >= static_cast<int>(sizeof(item->ip_addr)));

			memcpy(from, &item->ip_addr, sizeof(item->ip_addr));
			*fromlen = sizeof(item->ip_addr);
		}
	}

	// stick the item back on the free list
	multi_lag_put_free(item);

	// return the size in bytes
	return static_cast<int>(item->data_len);
}

// ----------------------------------------------------------------------------------------------------
// LAGLOSS FORWARD DEFINITIONS
//

int multi_lag_get_random_lag()
{
	// first determine the percentage we'll be checking against
	int ret;
	int mod;	

	// if the lag system isn't inited, don't do anything (no lag)
	if(!Multi_lag_inited){
		return 0;
	}
		
	// pick a value
	// see if we should be going up or down (loss max/loss min)
	float rand_val = Random::next() * Random::INV_F_MAX_VALUE;
	mod = 0;
	if (rand_val < 0.5f) {
		// down
		if (Multi_lag_min >= 0) {
			mod = -fl2i((Multi_lag_base - Multi_lag_min) * rand_val);
		}
	} else {
		// up
		if (Multi_lag_max >= 0) {
			mod = fl2i((Multi_lag_max - Multi_lag_base) * rand_val);
		}
	}
	
	// if the current streak has elapsed, calculate a new one
	if((Multi_streak_stamp == -1) || (timestamp_elapsed(Multi_streak_stamp))){
		// timestamp the new streak
		Multi_streak_stamp = timestamp(Multi_streak_time);

		// set the return value
		ret = Multi_lag_base + mod;
		
		// set the lag value of this current streak
		Multi_current_streak = ret;
	} 
	// otherwise use the lag for the current streak
	else {
		ret = Multi_current_streak;
	}
			
	return ret;	
}

// this _may_ be a bit heavyweight, but it _is_ debug code
int multi_lag_should_be_lost()
{	
	// first determine the percentage we'll be checking against
	float mod;

	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		return 0;
	}
		
	// see if we should be going up or down (loss max/loss min)
	float rand_val = Random::next() * Random::INV_F_MAX_VALUE;
	mod = 0.0f;
	if (rand_val < 0.5) {
		// down
		if (Multi_loss_min >= 0.0f) {
			mod = -((Multi_loss_base - Multi_loss_min) * rand_val);
		}
	} else {
		// up
		if (Multi_loss_max >= 0.0f) {
			mod = ((Multi_loss_max - Multi_loss_base) * rand_val);
		}
	}	
	
	if ( rand_val <= (Multi_loss_base + mod) ) {
		return 1;
	}	

	return 0;
}

// get a free packet buffer, return NULL on fail
lag_buf *multi_lag_get_free()
{
	lag_buf *lagp;

	// if we're out of buffers
	if(Lag_buf_count >= MAX_LAG_BUFFERS){
		nprintf(("Network", "Out of lag buffers!\n"));
		return NULL;
	}

	// get a free item
	lagp = GET_FIRST(&Lag_free_list);
	Assert( lagp != &Lag_free_list );		// shouldn't have the dummy element

	// remove trailp from the free list
	list_remove( &Lag_free_list, lagp );
	
	// insert trailp onto the end of used list
	list_append( &Lag_used_list, lagp );

	// increase the count
	Lag_buf_count++;
	return lagp;
}

// put a lag buffer back
void multi_lag_put_free(lag_buf *buf)
{
	// remove objp from the used list
	list_remove( &Lag_used_list, buf);

	// add objp to the end of the free
	list_append( &Lag_free_list, buf );

	// decrement counter
	Lag_buf_count--;
}

// Help and status provider for the lag-loss system
void multi_lagloss_dcf()
{
	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	// display status of lag system
	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("Lag system status:\n");
		// display lag settings
		dc_printf("Lag : \n");
		dc_printf("Base  \t\tMin   \t\tMax   \t\tStreak\n");
		dc_printf("%d\t\t%d\t\t%d\t\t%d\n\n", Multi_lag_base, Multi_lag_min, Multi_lag_max, Multi_streak_time);

		// display loss settings
		dc_printf("Loss : \n");
		dc_printf("Base  \t\tMin   \t\tMax\n");
		dc_printf("%f\t\t%f\t\t%f\n", Multi_loss_base, Multi_loss_min, Multi_loss_max);
		return;
	}

	// display all available commands
	dc_printf("Lag system commands\n\n");

	dc_printf("Usage :\n");
	dc_printf("lag <ms>\n");
		dc_printf("\tSets the lag base value if <ms> is within the max and min limits (see lag_min and lag_max)\n");
		dc_printf("\tIf <ms> is outside of the max and min limits, then nothing is done\n");
		dc_printf("\tIf <ms> is negative, then lag simulation is turned off\n\n");

	dc_printf("lag_min <ms>\n");
		dc_printf("\tSets the lag min value if <ms> is less than the base value\n");
		dc_printf("\tIf <ms> is outside the base or max values, then nothing is done\n");
		dc_printf("\tIf <ms> is negative, then the min limit is removed\n\n");

	dc_printf("lag_max <ms>\n");
		dc_printf("\tSets the lag max value if <ms> is greater than the base value\n");
		dc_printf("\tIf <ms> is outside the base or min values, then nothing is done\n");
		dc_printf("\tIf <ms> is negative, then the max limit is removed\n\n");

	dc_printf("loss    <0-100>  (-1 to disable)\n");
		dc_printf("\tSimilar to lag, but applies value to loss base value\n\n");

	dc_printf("loss_min <0-100>\n");
		dc_printf("\tSimilar to lag_min, but applies value to loss min value\n\n");
	
	dc_printf("loss_max <0-100>\n");
		dc_printf("\tSimilar to lag_max, but applies value to loss max value\n\n");


	dc_printf("lag_streak <ms>\n");
		dc_printf("\tSets the duration of lag streaks where the lag is consistant for <ms>\n");
		dc_printf("\tEx: A value of 2000 would result in lag streaks that last 2 seconds each\n\n");

	dc_printf("lagloss\n");
		dc_printf("\tDisplays this text. Passing --status will display the status of the entire lag system");
}

DCF(lag, "Sets the lag base value (Muliplayer)")
{
	int value;

	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("Lag base value is currently %i\n", Multi_lag_base);
		return;
	}

	dc_stuff_int(&value);
	// parse the argument and change things around accordingly
	if (value < 0) {
		// switch the lag sim off
		Multi_lag_base = -1;
		Multi_lag_min = -1;
		Multi_lag_max = -1;
		dc_printf("Turning simulated lag off\n");
		multi_lagloss_dcf();

	} else if ((Multi_lag_max >= 0) && (value > Multi_lag_max)) {
		dc_printf("Base value greater than max value, ignoring...");

	} else if ((Multi_lag_min >= 0) && (value < Multi_lag_min)) {
		dc_printf("Base value smaller than min value, ignoring...");

	} else {

		Multi_lag_base = value;
		multi_lagloss_dcf();
		dc_printf("Base value set to %i", value);
	}
}

DCF(lag_min, "Sets the lag min value (Multiplayer)")
{
	int value;

	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
	}

	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("Lag min value is currently %i\n", Multi_lag_min);
	}

	dc_stuff_int(&value);
	// parse the argument and change things around accordingly
	
	if (value > Multi_lag_base) {
		dc_printf("Min value greater than base value, ignoring...");
		return;

	} else if (value < 0) {
		Multi_lag_min = -1;

	} else {
		Multi_lag_min = value;
	}
	dc_printf("Lag min value set to %i\n", Multi_lag_min);
}

DCF(lag_max, "Sets the lag max value (Multiplayer)")
{
	int value;

	// if the lag system isn't inited, don't do anything
	if (!Multi_lag_inited) {
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("Lag max value is currently %i\n", Multi_lag_max);
		return;
	}

	// parse the argument and change things around accordingly
	dc_stuff_int(&value);
	
	if ((value >= 0) && (value < Multi_lag_base)) {
		dc_printf("Max value smaller than base value, ignoring...");

	} else if (value < 0) {
		Multi_lag_max = -1;

	} else {
		Multi_lag_max = value;
	}
	dc_printf("Lag max value set to %i\n", Multi_lag_max);
}

DCF(loss, "Sets the loss base value (Multiplayer)")
{
	int val_i;
	float val_f;

	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

		if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("Loss value is currently %f percent", Multi_loss_base);
		return;
	}

	// parse the argument and change things around accordingly
	dc_stuff_int(&val_i);

	val_f = (float)val_i / 100.0f;
		
	if(val_i > 100){
		dc_printf("Illegal loss base value, ignoring...");
	} else if (val_i < 0){
		// switch the loss sim off
		dc_printf("Turning simulated loss off\n");
		Multi_loss_base = -1.0f;
		Multi_loss_min = -1.0f;
		Multi_loss_max = -1.0f;

	} else if((Multi_loss_max >= 0.0f) && (val_f > Multi_loss_max)){
		dc_printf("Base value greater than max value, ignoring...");

	} else if((Multi_loss_min >= 0.0f) && (val_f < Multi_loss_min)){
		dc_printf("Base value smaller than min value, ignoring...");

	} else {
		Multi_loss_base = val_f;
	}
}

DCF(loss_min, "Sets the loss min value (Multiplayer)")
{
	int val_i;
	float val_f;

	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("loss_min value is currently %f percent", Multi_loss_min);
	}

	// parse the argument and change things around accordingly
	dc_stuff_int(&val_i);

	val_f = (float)val_i / 100.0f;

	if(val_f > Multi_loss_base){
		dc_printf("Min value greater than base value, ignoring...");
	} else {
		// otherwise set the value
		if (val_f < 0) {
			Multi_loss_min = -1.0f;
		} else {
			Multi_loss_min = val_f;
		}
	}
}

DCF(loss_max, "Sets the loss max value (Multiplayer)")
{
	int val_i;
	float val_f;
	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("loss_max value is currently %f percent", Multi_loss_max);
	}

	// parse the argument and change things around accordingly
	dc_stuff_int(&val_i);
	val_f = (float)val_i / 100.0f;

	if (val_f < Multi_loss_base) {
		dc_printf("Max value smaller than base value, ignoring...");
	} else {
		// otherwise set the value
		if (val_f < 0) {
			Multi_loss_max = -1.0f;
		} else {
			Multi_loss_max = val_f;
		}
	}
}

DCF(lagloss, "Help provider for the lag system (Multiplayer)")
{
	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	multi_lagloss_dcf();
}

DCF(lag_streak, "Sets the duration of lag streaks (Multiplayer)")
{
	int val;

	// if the lag system isn't inited, don't do anything
	if (!Multi_lag_inited) {
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
		dc_printf("lag_streak value is currently %i", Multi_streak_time);
		return;
	}

	dc_stuff_int(&val);
	if(val >= 0){
		Multi_streak_time = val;
	} else {
		dc_printf("Ignoring invalid value (must be non-negative)\n");
	}
}

DCF(lag_bad, "Lag system shortcut - Sets for 'bad' lag simulation (Multiplayer)")
{
	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}
	
	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	dc_printf("Setting bad lag/loss parameters\n");

	// set good lagloss parameters
	Multi_lag_base = 500;
	Multi_lag_min = 400;
	Multi_lag_max = 600;
	
	Multi_loss_base = 0.2f;
	Multi_loss_min = 0.15f;
	Multi_loss_max = 0.23f;

	Multi_streak_time = 800;
	Multi_streak_stamp = -1;
	Multi_current_streak = -1;
}

DCF(lag_avg, "Lag system shortcut - Sets for 'average' lag simulation (Multiplayer)")
{
	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	dc_printf("Setting avg lag/loss parameters\n");

	// set good lagloss parameters
	Multi_lag_base = 275;
	Multi_lag_min = 200;
	Multi_lag_max = 400;
	
	Multi_loss_base = 0.15f;
	Multi_loss_min = 0.1f;
	Multi_loss_max = 0.20f;

	Multi_streak_time = 900;
	Multi_streak_stamp = -1;
	Multi_current_streak = -1;
}

DCF(lag_good, "Lag system shortcut - Sets for 'good' lag simulation (Multiplayer)")
{
	// if the lag system isn't inited, don't do anything
	if(!Multi_lag_inited){
		dc_printf("Lag System Not Initialized!\n");
		return;
	}

	if (dc_optional_string_either("help", "--help")) {
		multi_lagloss_dcf();
		return;
	}

	dc_printf("Setting good lag/loss parameters\n");

	// set good lagloss parameters
	Multi_lag_base = 100;
	Multi_lag_min = 35;
	Multi_lag_max = 200;
	
	Multi_loss_base = 0.08f;
	Multi_loss_min = 0.0f;
	Multi_loss_max = 0.1f;

	Multi_streak_time = 1000;
	Multi_streak_stamp = -1;
	Multi_current_streak = -1;
}
