about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--default.bam1
-rw-r--r--src/engine/client/ec_client.c40
-rw-r--r--src/engine/e_config.h8
-rw-r--r--src/engine/e_console.c111
-rw-r--r--src/engine/e_console.h45
-rw-r--r--src/engine/e_engine.c35
-rw-r--r--src/engine/e_engine.h3
-rw-r--r--src/engine/e_if_modc.h4
-rw-r--r--src/engine/e_if_mods.h5
-rw-r--r--src/engine/e_server_interface.h2
-rw-r--r--src/engine/e_snapshot.h2
-rw-r--r--src/engine/e_system.h21
-rw-r--r--src/engine/server/es_server.c22
-rw-r--r--src/game/client/gc_client.cpp77
-rw-r--r--src/game/client/gc_client.h1
-rw-r--r--src/game/client/gc_console.cpp26
-rw-r--r--src/game/client/gc_hooks.cpp11
-rw-r--r--src/game/editor/ed_editor.hpp1
-rw-r--r--src/game/g_game.cpp79
-rw-r--r--src/game/g_game.h30
-rw-r--r--src/game/g_mapitems.h5
-rw-r--r--src/game/g_math.h26
-rw-r--r--src/game/g_protocol.h22
-rw-r--r--src/game/g_tuning.h17
-rw-r--r--src/game/g_variables.h4
-rw-r--r--src/game/server/gs_common.h2
-rw-r--r--src/game/server/gs_game_ctf.cpp2
-rw-r--r--src/game/server/gs_server.cpp71
28 files changed, 472 insertions, 201 deletions
diff --git a/default.bam b/default.bam
index 02c7e9b2..42fadfed 100644
--- a/default.bam
+++ b/default.bam
@@ -142,6 +142,7 @@ nethash = CHash(
 	"src/game/generated/nethash.c",
 	"src/engine/e_protocol.h",
 	"src/game/g_protocol.h",
+	"src/game/g_tuning.h",
 	"src/game/g_game.cpp", networkdata.header)
 
 client_link_other = {}
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c
index b1399645..3c0180b9 100644
--- a/src/engine/client/ec_client.c
+++ b/src/engine/client/ec_client.c
@@ -18,6 +18,7 @@
 #include <engine/e_packer.h>
 #include <engine/e_memheap.h>
 #include <engine/e_datafile.h>
+#include <engine/e_console.h>
 
 #include <mastersrv/mastersrv.h>
 
@@ -1207,20 +1208,49 @@ static void client_run()
 	snd_shutdown();
 }
 
+static void connect_command(void *result, void *user_data)
+{
+	const char *address;
+	console_result_string(result, 1, &address);
+	client_connect(address);
+}
+
+static void disconnect_command(void *result, void *user_data)
+{
+	client_disconnect();
+}
+
+static void quit_command(void *result, void *user_data)
+{
+	client_quit();
+}
+
+static void client_register_commands()
+{
+	MACRO_REGISTER_COMMAND("quit", "", quit_command, 0x0);
+	MACRO_REGISTER_COMMAND("connect", "s", connect_command, 0x0);
+	MACRO_REGISTER_COMMAND("disconnect", "", disconnect_command, 0x0);
+}
 
 int editor_main(int argc, char **argv);
 
 int main(int argc, char **argv)
 {
-	/* preinit the mod */
-	modc_preinit();
-
 	/* init the engine */
 	dbg_msg("client", "starting...");
-	engine_init("Teewars", argc, argv);
+	engine_init("Teewars");
 	
+	/* register all console commands */
+	client_register_commands();
+	modc_console_init();
+	
+	/* parse the command line arguments */
+	engine_parse_arguments(argc, argv);
+	
+	/* run the client*/
 	client_run();
-		
+	
+	/* write down the config and quit */	
 	engine_writeconfig();
 	return 0;
 }
diff --git a/src/engine/e_config.h b/src/engine/e_config.h
index b6fbc93d..f1145f6a 100644
--- a/src/engine/e_config.h
+++ b/src/engine/e_config.h
@@ -23,10 +23,10 @@ void config_reset();
 void config_load(const char *filename);
 void config_save(const char *filename);
 
-typedef int (*config_int_getter)(CONFIGURATION *c);
-typedef const char *(*config_str_getter)(CONFIGURATION *c);
-typedef void (*config_int_setter)(CONFIGURATION *c, int val);
-typedef void (*config_str_setter)(CONFIGURATION *c, const char *str);
+typedef int (*CONFIG_INT_GETTER)(CONFIGURATION *c);
+typedef const char *(*CONFIG_STR_GETTER)(CONFIGURATION *c);
+typedef void (*CONFIG_INT_SETTER)(CONFIGURATION *c, int val);
+typedef void (*CONFIG_STR_SETTER)(CONFIGURATION *c, const char *str);
 
 #define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c);
 #define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c);
diff --git a/src/engine/e_console.c b/src/engine/e_console.c
index 929a0eca..8582815b 100644
--- a/src/engine/e_console.c
+++ b/src/engine/e_console.c
@@ -1,9 +1,37 @@
+#include "e_system.h"
 #include "e_console.h"
 #include "e_config.h"
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 
+
+#define CONSOLE_MAX_STR_LENGTH 255
+/* the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces */
+#define MAX_TOKENS (CONSOLE_MAX_STR_LENGTH+1)/2
+ 
+enum 
+{ 
+    TOKEN_INT, 
+    TOKEN_FLOAT, 
+    TOKEN_STRING 
+}; 
+ 
+typedef struct
+{ 
+    int type; 
+	const char *stored_string;
+} TOKEN;
+ 
+typedef struct
+{ 
+	char string_storage[CONSOLE_MAX_STR_LENGTH+1];
+	char *next_string;
+
+    TOKEN tokens[MAX_TOKENS]; 
+    unsigned int num_tokens; 
+} LEXER_RESULT; 
+
 enum 
 { 
     STATE_START, 
@@ -15,7 +43,7 @@ enum
     STATE_ESCAPE 
 }; 
 
-static const char *store_string(struct lexer_result *res, const char *str, int len)
+static const char *store_string(LEXER_RESULT *res, const char *str, int len)
 {
 	const char *ptr = res->next_string;
 	int escaped = 0;
@@ -48,10 +76,10 @@ static const char *store_string(struct lexer_result *res, const char *str, int l
 	return ptr;
 }
 
-static void save_token(struct lexer_result *res, int *index, const char **start, const char *end, int *state, int type) 
+static void save_token(LEXER_RESULT *res, int *index, const char **start, const char *end, int *state, int type) 
 { 
     /* printf("Saving token with length %d\n", end - *start); */
-    struct token *tok = &res->tokens[*index]; 
+    TOKEN *tok = &res->tokens[*index]; 
 	tok->stored_string = store_string(res, *start, end - *start);
     tok->type = type; 
     ++res->num_tokens; 
@@ -66,14 +94,14 @@ static int digit(char c)
     return '0' <= c && c <= '9'; 
 } 
  
-static int lex(const char *line, struct lexer_result *res) 
+static int lex(const char *line, LEXER_RESULT *res) 
 { 
     int state = STATE_START, i = 0; 
 	int length_left = CONSOLE_MAX_STR_LENGTH;
     const char *start, *c; 
     res->num_tokens = 0; 
 
-	memset(res, 0, sizeof(*res));
+	mem_zero(res, sizeof(*res));
 	res->next_string = res->string_storage;
 
     for (c = start = line; *c != '\0' && res->num_tokens < MAX_TOKENS && length_left; ++c, --length_left) 
@@ -170,33 +198,35 @@ static int lex(const char *line, struct lexer_result *res)
 	return 0;
 }
 
-int extract_result_string(struct lexer_result *result, int index, const char **str)
+int console_result_string(void *res, int index, const char **str)
 {
+	LEXER_RESULT *result = (LEXER_RESULT *)res;
+	
 	if (index < 0 || index >= result->num_tokens)
 		return -1;
 	else
 	{
-		struct token *t = &result->tokens[index];
-
+		TOKEN *t = &result->tokens[index];
 		*str = t->stored_string;
-
 		return 0;
 	}
 }
 
-int extract_result_int(struct lexer_result *result, int index, int *i)
+int console_result_int(void *res, int index, int *i)
 {
+	LEXER_RESULT *result = (LEXER_RESULT *)res;
+	
 	if (index < 0 || index >= result->num_tokens)
 		return -1;
 	else
 	{
-		struct token *t = &result->tokens[index];
+		TOKEN *t = &result->tokens[index];
 		const char *str;
 
 		if (t->type != TOKEN_INT)
 			return -2;
 
-		extract_result_string(result, index, &str);
+		console_result_string(result, index, &str);
 
 		*i = atoi(str);
 
@@ -204,19 +234,21 @@ int extract_result_int(struct lexer_result *result, int index, int *i)
 	}
 }
 
-int extract_result_float(struct lexer_result *result, int index, float *f)
+int console_result_float(void *res, int index, float *f)
 {
+	LEXER_RESULT *result = (LEXER_RESULT *)res;
+	
 	if (index < 0 || index >= result->num_tokens)
 		return -1;
 	else
 	{
-		struct token *t = &result->tokens[index];
+		TOKEN *t = &result->tokens[index];
 		const char *str;
 
 		if (t->type != TOKEN_INT && t->type != TOKEN_FLOAT)
 			return -2;
 
-		extract_result_string(result, index, &str);
+		console_result_string(result, index, &str);
 
 		*f = atof(str);
 
@@ -243,7 +275,7 @@ void console_register(COMMAND *cmd)
 }
 
 
-static int console_validate(COMMAND *command, struct lexer_result *result)
+static int console_validate(COMMAND *command, LEXER_RESULT *result)
 {
 	const char *c = command->params;
 	int i = 1;
@@ -257,15 +289,15 @@ static int console_validate(COMMAND *command, struct lexer_result *result)
 		switch (*c)
 		{
 		case 's':
-			if (extract_result_string(result, i, &dummy_s))
+			if (console_result_string(result, i, &dummy_s))
 				return -1;
 			break;
 		case 'i':
-			if (extract_result_int(result, i, &dummy_i))
+			if (console_result_int(result, i, &dummy_i))
 				return -1;
 			break;
 		case 'f':
-			if (extract_result_float(result, i, &dummy_f))
+			if (console_result_float(result, i, &dummy_f))
 				return -1;
 			break;
 		default:
@@ -296,7 +328,7 @@ void console_print(const char *str)
 
 void console_execute(const char *str)
 {
-	struct lexer_result result;
+	LEXER_RESULT result;
 	int error;
 
 	if ((error = lex(str, &result)))
@@ -305,7 +337,7 @@ void console_execute(const char *str)
 	{
 		const char *name;
 		COMMAND *command;
-		extract_result_string(&result, 0, &name);
+		console_result_string(&result, 0, &name);
 
 		command = console_find_command(name);
 
@@ -329,33 +361,32 @@ void console_execute(const char *str)
 	}
 }
 
-static void echo_command(struct lexer_result *result, void *user_data)
+static void echo_command(void *result, void *user_data)
 {
 	const char *str;
-	extract_result_string(result, 1, &str);
-
+	console_result_string(result, 1, &str);
 	console_print(str);
 }
 
 
-struct int_variable_data
+typedef struct 
 {
-	config_int_getter getter;
-	config_int_setter setter;
-};
+	CONFIG_INT_GETTER getter;
+	CONFIG_INT_SETTER setter;
+} INT_VARIABLE_DATA;
 
-struct str_variable_data
+typedef struct
 {
-	config_str_getter getter;
-	config_str_setter setter;
-};
+	CONFIG_STR_GETTER getter;
+	CONFIG_STR_SETTER setter;
+} STR_VARIABLE_DATA;
 
-static void int_variable_command(struct lexer_result *result, void *user_data)
+static void int_variable_command(void *result, void *user_data)
 {
-	struct int_variable_data *data = (struct int_variable_data *)user_data;
+	INT_VARIABLE_DATA *data = (INT_VARIABLE_DATA *)user_data;
 	int new_val;
 
-	if (extract_result_int(result, 1, &new_val))
+	if (console_result_int(result, 1, &new_val))
 	{
 		char buf[256];
 		sprintf(buf, "Value: %d", data->getter(&config));
@@ -367,12 +398,12 @@ static void int_variable_command(struct lexer_result *result, void *user_data)
 	}
 }
 
-static void str_variable_command(struct lexer_result *result, void *user_data)
+static void str_variable_command(void *result, void *user_data)
 {
-	struct str_variable_data *data = (struct str_variable_data *)user_data;
+	STR_VARIABLE_DATA *data = (STR_VARIABLE_DATA *)user_data;
 	const char *new_val;
 
-	if (extract_result_string(result, 1, &new_val))
+	if (console_result_string(result, 1, &new_val))
 	{
 		char buf[256];
 		sprintf(buf, "Value: %s", data->getter(&config));
@@ -388,8 +419,8 @@ void console_init()
 {
 	MACRO_REGISTER_COMMAND("echo", "s", echo_command, 0x0);
 
-    #define MACRO_CONFIG_INT(name,def,min,max) { static struct int_variable_data data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", int_variable_command, &data) }
-    #define MACRO_CONFIG_STR(name,len,def) { static struct str_variable_data data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?s", str_variable_command, &data) }
+    #define MACRO_CONFIG_INT(name,def,min,max) { static INT_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", int_variable_command, &data) }
+    #define MACRO_CONFIG_STR(name,len,def) { static STR_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?s", str_variable_command, &data) }
 
     #include "e_config_variables.h" 
 
diff --git a/src/engine/e_console.h b/src/engine/e_console.h
index 77057df3..10d77a3a 100644
--- a/src/engine/e_console.h
+++ b/src/engine/e_console.h
@@ -5,46 +5,15 @@
 extern "C"{
 #endif
 
-#define CONSOLE_MAX_STR_LENGTH 255
-/* the maximum number of tokens occurs in a string of length CONSOLE_MAX_STR_LENGTH with tokens size 1 separated by single spaces */
-#define MAX_TOKENS (CONSOLE_MAX_STR_LENGTH+1)/2
- 
-enum 
-{ 
-    TOKEN_INT, 
-    TOKEN_FLOAT, 
-    TOKEN_STRING 
-}; 
- 
-struct token 
-{ 
-    int type; 
-	const char *stored_string;
-}; 
- 
-struct lexer_result 
-{ 
-	char string_storage[CONSOLE_MAX_STR_LENGTH+1];
-	char *next_string;
-
-    struct token tokens[MAX_TOKENS]; 
-    unsigned int num_tokens; 
-}; 
-
-int extract_result_string(struct lexer_result *result, int index, const char **str);
-int extract_result_int(struct lexer_result *result, int index, int *i);
-int extract_result_float(struct lexer_result *result, int index, float *f);
-
-typedef void (*console_callback)(struct lexer_result *result, void *user_data);
-
-typedef struct COMMAND
+typedef void (*CONSOLE_CALLBACK)(void *result, void *user_data);
+
+typedef struct COMMAND_t
 {
 	const char *name;
 	const char *params;
-	console_callback callback;
+	CONSOLE_CALLBACK callback;
 	void *user_data;
-	struct COMMAND *next;
-	
+	struct COMMAND_t *next;
 } COMMAND;
 
 void console_init();
@@ -53,6 +22,10 @@ void console_execute(const char *str);
 void console_print(const char *str);
 void console_register_print_callback(void (*callback)(const char *));
 
+int console_result_string(void *result, int index, const char **str);
+int console_result_int(void *result, int index, int *i);
+int console_result_float(void *result, int index, float *f);
+
 #define MACRO_REGISTER_COMMAND(name, params, func, ptr) { static COMMAND cmd = { name, params, func, ptr, 0x0 }; console_register(&cmd); }
 
 #ifdef __cplusplus
diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c
index 5cd5d6bd..3168020d 100644
--- a/src/engine/e_engine.c
+++ b/src/engine/e_engine.c
@@ -47,28 +47,29 @@ void engine_init(const char *appname, int argc, char **argv)
 	
 	/* reset the config */
 	config_reset();
-	
+}
+
+void engine_parse_arguments(int argc, char **argv)
+{
 	/* load the configuration */
+	int i;
+	int abs = 0;
+	const char *config_filename = "default.cfg";
+	char buf[1024];
+	for(i = 1; i < argc; i++)
 	{
-		int i;
-		int abs = 0;
-		const char *config_filename = "default.cfg";
-		char buf[1024];
-		for(i = 1; i < argc; i++)
+		if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1)
 		{
-			if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1)
-			{
-				config_filename = argv[i+1];
-				abs = 1;
-				i++;
-			}
+			config_filename = argv[i+1];
+			abs = 1;
+			i++;
 		}
-
-		if(abs)
-			config_load(config_filename);
-		else
-			config_load(engine_savepath(config_filename, buf, sizeof(buf)));
 	}
+
+	if(abs)
+		config_load(config_filename);
+	else
+		config_load(engine_savepath(config_filename, buf, sizeof(buf)));
 	
 	/* search arguments for overrides */
 	{
diff --git a/src/engine/e_engine.h b/src/engine/e_engine.h
index 56fec20e..ba880bb0 100644
--- a/src/engine/e_engine.h
+++ b/src/engine/e_engine.h
@@ -1,5 +1,6 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 
 const char *engine_savepath(const char *filename, char *buffer, int max);
-void engine_init(const char *appname, int argc, char **argv);
+void engine_init(const char *appname);
+void engine_parse_arguments(int argc, char **argv);
 void engine_writeconfig();
diff --git a/src/engine/e_if_modc.h b/src/engine/e_if_modc.h
index 581b0a67..33069a6f 100644
--- a/src/engine/e_if_modc.h
+++ b/src/engine/e_if_modc.h
@@ -6,10 +6,10 @@
 	Section: Client Hooks
 *********************************************************************************/
 /*
-	Function: modc_preinit
+	Function: modc_console_init
 		TODO
 */
-void modc_preinit();
+void modc_console_init();
 
 /*
 	Function: modc_init
diff --git a/src/engine/e_if_mods.h b/src/engine/e_if_mods.h
index 1e6d9694..eff909f3 100644
--- a/src/engine/e_if_mods.h
+++ b/src/engine/e_if_mods.h
@@ -5,6 +5,11 @@
 /**********************************************************************************
 	Section: Server Hooks
 **********************************************************************************/
+/*
+	Function: mods_console_init
+		TODO
+*/
+void mods_console_init();
 
 /*
 	Function: mods_init
diff --git a/src/engine/e_server_interface.h b/src/engine/e_server_interface.h
index f1749fcd..5b1e6327 100644
--- a/src/engine/e_server_interface.h
+++ b/src/engine/e_server_interface.h
@@ -11,6 +11,8 @@ extern "C" {
 #include "e_if_msg.h"
 #include "e_if_mods.h"
 
+#include "e_console.h" /* TODO: clean this up*/
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/engine/e_snapshot.h b/src/engine/e_snapshot.h
index 0a74b0dc..1396f6ee 100644
--- a/src/engine/e_snapshot.h
+++ b/src/engine/e_snapshot.h
@@ -70,7 +70,7 @@ int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data);
 
 enum
 {
-	SNAPBUILD_MAX_ITEMS = 512
+	SNAPBUILD_MAX_ITEMS = 1024*2
 };
 
 typedef struct SNAPBUILD
diff --git a/src/engine/e_system.h b/src/engine/e_system.h
index 72a4ff7b..69fa5c63 100644
--- a/src/engine/e_system.h
+++ b/src/engine/e_system.h
@@ -161,8 +161,7 @@ typedef struct IOINTERNAL *IOHANDLE;
 
 /****
 	Function: io_open
-	
-	Opens a file.
+		Opens a file.
 
 	Parameters:
 		filename - File to open.
@@ -176,8 +175,7 @@ IOHANDLE io_open(const char *filename, int flags);
 
 /****
 	Function: io_read
-	
-	Reads data into a buffer from a file.
+		Reads data into a buffer from a file.
 
 	Parameters:
 		io - Handle to the file to read data from.
@@ -192,8 +190,7 @@ unsigned io_read(IOHANDLE io, void *buffer, unsigned size);
 
 /*****
 	Function: io_skip
-	
-	Skips data in a file.
+		Skips data in a file.
 	
 	Parameters:
 		io - Handle to the file.
@@ -221,8 +218,7 @@ unsigned io_write(IOHANDLE io, const void *buffer, unsigned size);
 
 /*****
 	Function: io_seek
-	
-	Seeks to a specified offset in the file.
+		Seeks to a specified offset in the file.
 	
 	Parameters:
 		io - Handle to the file.
@@ -236,8 +232,7 @@ int io_seek(IOHANDLE io, int offset, int origin);
 
 /*****
 	Function: io_tell
-	
-	Gets the current position in the file.
+		Gets the current position in the file.
 	
 	Parameters:
 		io - Handle to the file.
@@ -249,8 +244,7 @@ long int io_tell(IOHANDLE io);
 
 /*****
 	Function: io_length
-	
-	Gets the total length of the file. Resetting cursor to the beginning
+		Gets the total length of the file. Resetting cursor to the beginning
 	
 	Parameters:
 		io - Handle to the file.
@@ -262,8 +256,7 @@ long int io_length(IOHANDLE io);
 
 /*****
 	Function: io_close
-	
-	Closes a file.
+		Closes a file.
 	
 	Parameters:
 		io - Handle to the file.
diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c
index d781c8da..8d805e42 100644
--- a/src/engine/server/es_server.c
+++ b/src/engine/server/es_server.c
@@ -225,6 +225,9 @@ void server_setbrowseinfo(int game_type, int progression)
 
 void server_kick(int client_id, const char *reason)
 {
+	if(client_id < 0 || client_id > MAX_CLIENTS)
+		return;
+		
 	if(clients[client_id].state != SRVCLIENT_STATE_EMPTY)
 		netserver_drop(net, client_id, reason);
 }
@@ -647,7 +650,7 @@ static void server_process_client_packet(NETPACKET *packet)
 			if(config.rcon_password[0] != 0 && strcmp(pw, config.rcon_password) == 0)
 			{
 				dbg_msg("server", "cid=%d rcon='%s'", cid, cmd);
-				config_set(cmd);
+				console_execute(cmd);
 			}
 		}
 		else
@@ -1039,11 +1042,26 @@ static int server_run()
 	return 0;
 }
 
+static void server_register_commands()
+{
+	
+}
+
 
 int main(int argc, char **argv)
 {
+	/* init the engine */
 	dbg_msg("server", "starting...");
-	engine_init("Teewars", argc, argv);
+	engine_init("Teewars");
+	
+	/* register all console commands */
+	server_register_commands();
+	mods_console_init();
+	
+	/* parse the command line arguments */
+	engine_parse_arguments(argc, argv);
+	
+	/* run the server */
 	server_run();
 	return 0;
 }
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index 27021c1f..c092ca73 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -35,6 +35,7 @@ bool menu_active = false;
 bool menu_game_active = false;
 static bool emoticon_selector_active = false;
 
+tuning_params tuning;
 
 vec2 mouse_pos;
 vec2 local_character_pos;
@@ -1439,14 +1440,6 @@ void render_game()
 		}
 	}
 	
-	if(client_connection_problems())
-	{
-		gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300);
-		const char *text = "Connection Problems...";
-		float w = gfx_text_width(0, 24, text, -1);
-		gfx_text(0, 150*gfx_screenaspect()-w/2, 50, 24, text, -1);
-	}
-	
 	if(config.debug && local_character && local_prev_character)
 	{
 		gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300);
@@ -1496,5 +1489,73 @@ void render_game()
 		render_goals(width/2-w/2, 150+750+25, w);
 		render_spectators(width/2-w/2, 150+750+25+50+25, w);
 	}
+	
+	
+	
+	{
+		gfx_mapscreen(0, 0, 300*gfx_screenaspect(), 300);
+
+		if(client_connection_problems())
+		{
+			const char *text = "Connection Problems...";
+			float w = gfx_text_width(0, 24, text, -1);
+			gfx_text(0, 150*gfx_screenaspect()-w/2, 50, 24, text, -1);
+		}
+		
+		tuning_params standard_tuning;
+
+		// render warning about non standard tuning
+		bool flash = time_get()/(time_freq()/2)%2 == 0;
+		if(config.cl_warning_tuning && memcmp(&standard_tuning, &tuning, sizeof(tuning_params)) != 0)
+		{
+			const char *text = "Warning! Server is running non-standard tuning.";
+			if(flash)
+				gfx_text_color(1,0.4f,0.4f,1.0f);
+			else
+				gfx_text_color(0.75f,0.2f,0.2f,1.0f);
+			gfx_text(0x0, 5, 40, 6, text, -1);
+			gfx_text_color(1,1,1,1);
+		}
+		
+		// render tuning debugging
+		if(config.dbg_tuning)
+		{
+			float y = 50.0f;
+			int count = 0;
+			for(int i = 0; i < tuning.num(); i++)
+			{
+				char buf[128];
+				float current, standard;
+				tuning.get(i, &current);
+				standard_tuning.get(i, &standard);
+				
+				if(standard == current)
+					gfx_text_color(1,1,1,1.0f);
+				else
+					gfx_text_color(1,0.25f,0.25f,1.0f);
+		
+				float w;
+				float x = 5.0f;
+				
+				sprintf(buf, "%.2f", standard);
+				x += 20.0f;
+				w = gfx_text_width(0, 5, buf, -1);
+				gfx_text(0x0, x-w, y+count*6, 5, buf, -1);
+
+				sprintf(buf, "%.2f", current);
+				x += 20.0f;
+				w = gfx_text_width(0, 5, buf, -1);
+				gfx_text(0x0, x-w, y+count*6, 5, buf, -1);
+
+				x += 5.0f;
+				gfx_text(0x0, x, y+count*6, 5, tuning.names[i], -1);
+				
+				count++;
+			}
+		}
+		
+		gfx_text_color(1,1,1,1);
+	}
+	
 }
 
diff --git a/src/game/client/gc_client.h b/src/game/client/gc_client.h
index def3cad8..63c74d77 100644
--- a/src/game/client/gc_client.h
+++ b/src/game/client/gc_client.h
@@ -25,6 +25,7 @@ extern const obj_player_character *local_prev_character;
 extern const obj_player_info *local_info;
 extern const obj_flag *flags[2];
 extern const obj_game *gameobj;
+extern tuning_params tuning;
 
 // predicted players
 extern player_core predicted_prev_player;
diff --git a/src/game/client/gc_console.cpp b/src/game/client/gc_console.cpp
index b8f090ea..d462156b 100644
--- a/src/game/client/gc_console.cpp
+++ b/src/game/client/gc_console.cpp
@@ -39,6 +39,8 @@ static float time_now()
 	return float(time_get()-time_start)/float(time_freq());
 }
 
+
+
 static void client_console_print(const char *str)
 {
 	int len = strlen(str);
@@ -66,39 +68,21 @@ static void client_console_print(const char *str)
 	//dbg_msg("console", "FROM CLIENT!! %s", str);
 }
 
-static void connect_command(struct lexer_result *result, void *user_data)
-{
-	const char *address;
-	extract_result_string(result, 1, &address);
-	client_connect(address);
-}
 
-static void disconnect_command(struct lexer_result *result, void *user_data)
-{
-	client_disconnect();
-}
-
-static void quit_command(struct lexer_result *result, void *user_data)
-{
-	client_quit();
-}
-
-static void con_team(struct lexer_result *result, void *user_data)
+static void con_team(void *result, void *user_data)
 {
 	int new_team;
-	extract_result_int(result, 1, &new_team);
+	console_result_int(result, 1, &new_team);
 	send_switch_team(new_team);
 }
 
 void client_console_init()
 {
 	console_register_print_callback(client_console_print);
-	MACRO_REGISTER_COMMAND("quit", "", quit_command, 0x0);
-	MACRO_REGISTER_COMMAND("connect", "s", connect_command, 0x0);
-	MACRO_REGISTER_COMMAND("disconnect", "", disconnect_command, 0x0);
 	MACRO_REGISTER_COMMAND("team", "i", con_team, 0x0);
 }
 
+
 void console_handle_input()
 {
 	int was_active = console_active();
diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp
index 3c8d801d..0acd1c41 100644
--- a/src/game/client/gc_hooks.cpp
+++ b/src/game/client/gc_hooks.cpp
@@ -5,6 +5,7 @@
 extern "C" {
 	#include <engine/e_config.h>
 	#include <engine/client/ec_font.h>
+	#include <engine/e_console.h>
 };
 
 #include <game/generated/gc_data.h>
@@ -25,7 +26,7 @@ extern void menu_init();
 extern bool menu_active;
 extern bool menu_game_active;
 
-extern "C" void modc_preinit()
+extern "C" void modc_console_init()
 {
 	client_console_init();
 }
@@ -109,6 +110,7 @@ extern "C" void modc_predict()
 
 	// repredict player
 	world_core world;
+	world.tuning = tuning;
 	int local_cid = -1;
 
 	// search for players
@@ -437,10 +439,15 @@ extern "C" void modc_message(int msg)
 		
 		client_datas[cid].update_render_info();
 	}
+	else if(msg == MSG_TUNE_PARAMS)
+	{
+		int *params = (int *)&tuning;
+		for(unsigned i = 0; i < sizeof(tuning_params)/sizeof(int); i++)
+			params[i] = msg_unpack_int();
+	}
     else if(msg == MSG_WEAPON_PICKUP)
     {
         int weapon = msg_unpack_int();
-
         picked_up_weapon = weapon+1;
     }
 	else if(msg == MSG_READY_TO_ENTER)
diff --git a/src/game/editor/ed_editor.hpp b/src/game/editor/ed_editor.hpp
index 80ca42d5..2bbc4e08 100644
--- a/src/game/editor/ed_editor.hpp
+++ b/src/game/editor/ed_editor.hpp
@@ -4,6 +4,7 @@
 #include <math.h>
 #include "array.h"
 #include "../g_mapitems.h"
+#include "../g_math.h"
 #include "../client/gc_render.h"
 
 extern "C" {
diff --git a/src/game/g_game.cpp b/src/game/g_game.cpp
index 98ae8c47..74b9aed4 100644
--- a/src/game/g_game.cpp
+++ b/src/game/g_game.cpp
@@ -1,6 +1,50 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <string.h>
 #include "g_game.h"
 
+const char *tuning_params::names[] =
+{
+	#define MACRO_TUNING_PARAM(name,value) #name,
+	#include "g_tuning.h"
+	#undef MACRO_TUNING_PARAM
+};
+
+
+bool tuning_params::set(int index, float value)
+{
+	if(index < 0 || index >= num())
+		return false;
+	((tune_param *)this)[index] = value;
+	return true;
+}
+
+bool tuning_params::get(int index, float *value)
+{
+	if(index < 0 || index >= num())
+		return false;
+	*value = (float)((tune_param *)this)[index];
+	return true;
+}
+
+bool tuning_params::set(const char *name, float value)
+{
+	for(int i = 0; i < num(); i++)
+		if(strcmp(name, names[i]) == 0)
+			return set(i, value);
+	return false;
+}
+
+bool tuning_params::get(const char *name, float *value)
+{
+	for(int i = 0; i < num(); i++)
+		if(strcmp(name, names[i]) == 0)
+			return get(i, value);
+	
+	return false;
+}
+
+
+
 // TODO: OPT: rewrite this smarter!
 void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces)
 {
@@ -125,11 +169,11 @@ void player_core::tick()
 	
 	vec2 direction = normalize(vec2(input.target_x, input.target_y));
 
-	vel.y += gravity;
+	vel.y += world->tuning.gravity;
 	
-	float max_speed = grounded ? ground_control_speed : air_control_speed;
-	float accel = grounded ? ground_control_accel : air_control_accel;
-	float friction = grounded ? ground_friction : air_friction;
+	float max_speed = grounded ? world->tuning.ground_control_speed : world->tuning.air_control_speed;
+	float accel = grounded ? world->tuning.ground_control_accel : world->tuning.air_control_accel;
+	float friction = grounded ? world->tuning.ground_friction : world->tuning.air_friction;
 	
 	// handle movement
 	if(input.left)
@@ -153,13 +197,13 @@ void player_core::tick()
 			if(grounded)
 			{
 				triggered_events |= COREEVENT_GROUND_JUMP;
-				vel.y = -ground_jump_speed;
+				vel.y = -world->tuning.ground_jump_impulse;
 				jumped |= 1;
 			}
 			else if(!(jumped&2))
 			{
 				triggered_events |= COREEVENT_AIR_JUMP;
-				vel.y = -ground_air_speed;
+				vel.y = -world->tuning.air_jump_impulse;
 				jumped |= 3;
 			}
 		}
@@ -181,7 +225,7 @@ void player_core::tick()
 		}
 		else if(hook_state == HOOK_FLYING)
 		{
-			vec2 new_pos = hook_pos+hook_dir*hook_fire_speed;
+			vec2 new_pos = hook_pos+hook_dir*world->tuning.hook_fire_speed;
 
 			// Check against other players first
 			for(int i = 0; i < MAX_CLIENTS; i++)
@@ -223,7 +267,7 @@ void player_core::tick()
 					hook_state = HOOK_GRABBED;
 					hook_pos = new_pos;	
 				}
-				else if(distance(pos, new_pos) > hook_length)
+				else if(distance(pos, new_pos) > world->tuning.hook_length)
 				{
 					triggered_events |= COREEVENT_HOOK_RETRACT;
 					hook_state = HOOK_RETRACTED;
@@ -270,7 +314,7 @@ void player_core::tick()
 		// don't do this hook rutine when we are hook to a player
 		if(hooked_player == -1 && distance(hook_pos, pos) > 46.0f)
 		{
-			vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel;
+			vec2 hookvel = normalize(hook_pos-pos)*world->tuning.hook_drag_accel;
 			// the hook as more power to drag you up then down.
 			// this makes it easier to get on top of an platform
 			if(hookvel.y > 0)
@@ -286,7 +330,7 @@ void player_core::tick()
 			vec2 new_vel = vel+hookvel;
 
 			// check if we are under the legal limit for the hook
-			if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
+			if(length(new_vel) < world->tuning.hook_drag_speed || length(new_vel) < length(vel))
 				vel = new_vel; // no problem. apply
 				
 		}
@@ -333,23 +377,24 @@ void player_core::tick()
 			{
 				if(d > phys_size*1.50f) // TODO: fix tweakable variable
 				{
-					float accel = hook_drag_accel * (d/hook_length);
+					float accel = world->tuning.hook_drag_accel * (d/world->tuning.hook_length);
+					float drag_speed = world->tuning.hook_drag_speed;
 					
 					// add force to the hooked player
-					p->vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, p->vel.x, accel*dir.x*1.5f);
-					p->vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, p->vel.y, accel*dir.y*1.5f);
+					p->vel.x = saturated_add(-drag_speed, drag_speed, p->vel.x, accel*dir.x*1.5f);
+					p->vel.y = saturated_add(-drag_speed, drag_speed, p->vel.y, accel*dir.y*1.5f);
 
 					// add a little bit force to the guy who has the grip
-					vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x*0.25f);
-					vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y*0.25f);
+					vel.x = saturated_add(-drag_speed, drag_speed, vel.x, -accel*dir.x*0.25f);
+					vel.y = saturated_add(-drag_speed, drag_speed, vel.y, -accel*dir.y*0.25f);
 				}
 			}
 		}
 	}	
 
 	// clamp the velocity to something sane
-	if(length(vel) > terminal_velocity)
-		vel = normalize(vel) * terminal_velocity;
+	if(length(vel) > world->tuning.terminal_velocity)
+		vel = normalize(vel) * world->tuning.terminal_velocity;
 }
 
 void player_core::move()
diff --git a/src/game/g_game.h b/src/game/g_game.h
index 5cece9d9..a9fddde7 100644
--- a/src/game/g_game.h
+++ b/src/game/g_game.h
@@ -4,12 +4,35 @@
 
 #include <engine/e_system.h>
 #include <engine/e_common_interface.h>
-#include <game/g_math.h>
 #include <math.h>
+#include "g_math.h"
 #include "g_collision.h"
-
 #include "g_protocol.h"
 
+struct tuning_params
+{
+	tuning_params()
+	{
+		const float ticks_per_second = 50.0f;
+		#define MACRO_TUNING_PARAM(name,value) name = value;
+		#include "g_tuning.h"
+		#undef MACRO_TUNING_PARAM
+	}
+
+	static const char *names[];
+	
+	#define MACRO_TUNING_PARAM(name,value) tune_param name;
+	#include "g_tuning.h"
+	#undef MACRO_TUNING_PARAM
+	
+	static int num() { return sizeof(tuning_params)/sizeof(int); }
+	bool set(int index, float value);
+	bool set(const char *name, float value);
+	bool get(int index, float *value);
+	bool get(const char *name, float *value);
+};
+
+
 inline vec2 get_direction(int angle)
 {
 	float a = angle/256.0f;
@@ -90,7 +113,8 @@ public:
 	{
 		mem_zero(players, sizeof(players));
 	}
-		
+	
+	tuning_params tuning;
 	class player_core *players[MAX_CLIENTS];
 };
 
diff --git a/src/game/g_mapitems.h b/src/game/g_mapitems.h
index 8b4455ec..53561beb 100644
--- a/src/game/g_mapitems.h
+++ b/src/game/g_mapitems.h
@@ -158,9 +158,4 @@ typedef struct
 	int name;
 } MAPITEM_ENVELOPE;
 
-// float to fixed
-inline int f2fx(float v) { return (int)(v*(float)(1<<10)); }
-inline float fx2f(int v) { return v*(1.0f/(1<<10)); }
-
-
 #endif
diff --git a/src/game/g_math.h b/src/game/g_math.h
index 54c23ac9..5e3f7ede 100644
--- a/src/game/g_math.h
+++ b/src/game/g_math.h
@@ -27,6 +27,32 @@ inline T mix(const T a, const T b, TB amount)
 
 inline float frandom() { return rand()/(float)(RAND_MAX); }
 
+// float to fixed
+inline int f2fx(float v) { return (int)(v*(float)(1<<10)); }
+inline float fx2f(int v) { return v*(1.0f/(1<<10)); }
+
+class fxp
+{
+	int value;
+public:
+	void set(int v) { value = v; }
+	int get() const { return value; }
+	fxp &operator = (int v) { value = v<<10; return *this; }
+	fxp &operator = (float v) { value = (int)(v*(float)(1<<10)); return *this; }
+	operator float() const { return value/(float)(1<<10); }
+};
+
+class tune_param
+{
+	int value;
+public:
+	void set(int v) { value = v; }
+	int get() const { return value; }
+	tune_param &operator = (int v) { value = (int)(v*100.0f); return *this; }
+	tune_param &operator = (float v) { value = (int)(v*100.0f); return *this; }
+	operator float() const { return value/100.0f; }
+};
+
 const float pi = 3.1415926535897932384626433f;
 
 template <typename T> inline T min(T a, T b) { return a<b?a:b; }
diff --git a/src/game/g_protocol.h b/src/game/g_protocol.h
index e63da143..b0d84916 100644
--- a/src/game/g_protocol.h
+++ b/src/game/g_protocol.h
@@ -4,27 +4,6 @@
 #ifndef GAME_PROTOCOL_H
 #define GAME_PROTOCOL_H
 
-// --------- PHYSICS TWEAK! --------
-const float ticks_per_second = 50.0f;
-const float ground_control_speed = 350.0f / ticks_per_second;
-const float ground_control_accel = 100.0f / ticks_per_second;
-const float ground_friction = 0.5f;
-const float ground_jump_speed = 12.6f;
-const float ground_air_speed = 11.5f;
-const float air_control_speed = 250.0f / ticks_per_second;
-const float air_control_accel = 1.5f;
-const float air_friction = 0.95f;
-const float hook_length = 34*10.0f;
-const float hook_fire_speed = 45.0f;
-const float hook_drag_accel = 3.0f;
-const float hook_drag_speed = 15.0f;
-const float gravity = 0.5f;
-const float terminal_velocity = 20.0f;
-
-const float wall_friction = 0.80f;
-const float wall_jump_speed_up = ground_jump_speed*0.8f;
-const float wall_jump_speed_out = ground_jump_speed*0.8f;
-
 // Network stuff
 enum
 {
@@ -62,6 +41,7 @@ enum
 	MSG_READY_TO_ENTER, // server -> client
     MSG_WEAPON_PICKUP,
     MSG_SOUND_GLOBAL,
+    MSG_TUNE_PARAMS,
 };
 
 enum
diff --git a/src/game/g_tuning.h b/src/game/g_tuning.h
new file mode 100644
index 00000000..9f7ffb80
--- /dev/null
+++ b/src/game/g_tuning.h
@@ -0,0 +1,17 @@
+/* physics tuning */
+MACRO_TUNING_PARAM(ground_control_speed, 350.0f / ticks_per_second)
+MACRO_TUNING_PARAM(ground_control_accel, 100.0f / ticks_per_second)
+MACRO_TUNING_PARAM(ground_friction, 0.5f)
+MACRO_TUNING_PARAM(ground_jump_impulse, 12.6f)
+MACRO_TUNING_PARAM(air_jump_impulse, 11.5f)
+MACRO_TUNING_PARAM(air_control_speed, 250.0f / ticks_per_second)
+MACRO_TUNING_PARAM(air_control_accel, 1.5f)
+MACRO_TUNING_PARAM(air_friction, 0.95f)
+MACRO_TUNING_PARAM(hook_length, 34*10.0f)
+MACRO_TUNING_PARAM(hook_fire_speed, 45.0f)
+MACRO_TUNING_PARAM(hook_drag_accel, 3.0f)
+MACRO_TUNING_PARAM(hook_drag_speed, 15.0f)
+MACRO_TUNING_PARAM(gravity, 0.5f)
+MACRO_TUNING_PARAM(terminal_velocity, 1000.0f / ticks_per_second)
+
+/* weapon tuning */
diff --git a/src/game/g_variables.h b/src/game/g_variables.h
index 9e4d1c7c..d7848629 100644
--- a/src/game/g_variables.h
+++ b/src/game/g_variables.h
@@ -34,6 +34,9 @@ MACRO_CONFIG_INT(cl_autoswitch_weapons, 0, 0, 1)
 MACRO_CONFIG_INT(cl_show_player_ids, 0, 0, 1)
 
 
+MACRO_CONFIG_INT(cl_warning_tuning, 1, 0, 1)
+
+
 MACRO_CONFIG_INT(cl_flow, 0, 0, 1)
 
 MACRO_CONFIG_INT(cl_show_welcome, 1, 0, 1)
@@ -46,6 +49,7 @@ MACRO_CONFIG_STR(player_skin, 64, "default")
 MACRO_CONFIG_INT(dbg_bots, 0, 0, 11)
 MACRO_CONFIG_INT(dbg_firedelay, 0, 0, 1)
 MACRO_CONFIG_INT(dbg_flow, 0, 0, 1)
+MACRO_CONFIG_INT(dbg_tuning, 0, 0, 1)
 
 MACRO_CONFIG_INT(ui_page, 1, 0, 5)
 MACRO_CONFIG_STR(ui_server_address, 128, "localhost:8303")
diff --git a/src/game/server/gs_common.h b/src/game/server/gs_common.h
index 8c4509a2..4519cf37 100644
--- a/src/game/server/gs_common.h
+++ b/src/game/server/gs_common.h
@@ -3,6 +3,8 @@
 #include "../generated/gs_data.h"
 
 
+extern tuning_params tuning;
+
 void create_sound_global(int sound, int target=-1);
 
 inline int cmask_all() { return -1; }
diff --git a/src/game/server/gs_game_ctf.cpp b/src/game/server/gs_game_ctf.cpp
index 0c54d1c5..37e2baba 100644
--- a/src/game/server/gs_game_ctf.cpp
+++ b/src/game/server/gs_game_ctf.cpp
@@ -151,7 +151,7 @@ void gameobject_ctf::tick()
 				}
 				else
 				{
-					f->vel.y += gravity;
+					f->vel.y += world->core.tuning.gravity;
 					move_box(&f->pos, &f->vel, vec2(f->phys_size, f->phys_size), 0.5f);
 				}
 			}
diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp
index c1f5e130..0416c808 100644
--- a/src/game/server/gs_server.cpp
+++ b/src/game/server/gs_server.cpp
@@ -14,6 +14,8 @@
 
 data_container *data = 0x0;
 
+tuning_params tuning;
+
 class player* get_player(int index);
 void create_damageind(vec2 p, float angle_mod, int amount);
 void create_explosion(vec2 p, int owner, int weapon, bool bnodamage);
@@ -58,6 +60,16 @@ void send_chat(int cid, int team, const char *msg)
 }
 
 
+void send_tuning_params(int cid)
+{
+	msg_pack_start(MSG_TUNE_PARAMS, MSGFLAG_VITAL);
+	int *params = (int *)&tuning;
+	for(unsigned i = 0; i < sizeof(tuning_params)/sizeof(int); i++)
+		msg_pack_int(params[i]);
+	msg_pack_end();
+	server_send_msg(cid);
+}
+
 //////////////////////////////////////////////////
 // Event handler
 //////////////////////////////////////////////////
@@ -1670,7 +1682,7 @@ player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis)
 // Server hooks
 void mods_tick()
 {
-	// clear all events
+	world->core.tuning = tuning;
 	world->tick();
 
 	if(world->paused) // make sure that the game object always updates
@@ -1858,13 +1870,19 @@ void mods_message(int msg, int client_id)
 		
 		if(msg == MSG_STARTINFO)
 		{
+			// a client that connected!
+			
 			// send all info to this client
 			for(int i = 0; i < MAX_CLIENTS; i++)
 			{
 				if(players[i].client_id != -1)
 					send_info(i, client_id);
 			}
+
+			// send tuning parameters to client
+			send_tuning_params(client_id);
 			
+			//
 			msg_pack_start(MSG_READY_TO_ENTER, MSGFLAG_VITAL);
 			msg_pack_end();
 			server_send_msg(client_id);			
@@ -1881,6 +1899,57 @@ void mods_message(int msg, int client_id)
 
 extern unsigned char internal_data[];
 
+
+static void con_tune_param(void *result, void *user_data)
+{
+	const char *param_name;
+	float new_value;
+
+	if(console_result_string(result, 1, &param_name) == 0)
+	{
+		if(console_result_float(result, 2, &new_value) == 0)
+		{
+			if(tuning.set(param_name, new_value))
+			{
+				dbg_msg("tuning", "%s changed to %.2f", param_name, new_value);
+				send_tuning_params(-1);
+			}
+			else
+				console_print("No such tuning parameter");
+		}
+		else
+		{
+			//console_print("");
+		}
+	}
+}
+
+static void con_tune_reset(void *result, void *user_data)
+{
+	tuning_params p;
+	tuning = p;
+	send_tuning_params(-1);
+	console_print("tuning reset");
+}
+
+static void con_tune_dump(void *result, void *user_data)
+{
+	for(int i = 0; i < tuning.num(); i++)
+	{
+		float v;
+		tuning.get(i, &v);
+		dbg_msg("tuning", "%s %.2f", tuning.names[i], v);
+	}
+}
+
+
+void mods_console_init()
+{
+	MACRO_REGISTER_COMMAND("tune", "s?i", con_tune_param, 0);
+	MACRO_REGISTER_COMMAND("tune_reset", "", con_tune_reset, 0);
+	MACRO_REGISTER_COMMAND("tune_dump", "", con_tune_dump, 0);
+}
+
 void mods_init()
 {
 	if(!data) /* only load once */