about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakob Fries <jakob.fries@gmail.com>2008-01-16 22:14:06 +0000
committerJakob Fries <jakob.fries@gmail.com>2008-01-16 22:14:06 +0000
commit75c8b2e9a58e7052f484e43291baa2d9ae1a384a (patch)
tree40e065a48cae7e840cc6e8f9fc9771c500910f4a
parenta9c90d6ac082dbfea5590c54d5be7a71dd112b5d (diff)
downloadzcatch-75c8b2e9a58e7052f484e43291baa2d9ae1a384a.tar.gz
zcatch-75c8b2e9a58e7052f484e43291baa2d9ae1a384a.zip
Added first version of the console.
-rw-r--r--src/engine/e_config.c1
-rw-r--r--src/engine/e_config.h6
-rw-r--r--src/engine/e_console.c368
-rw-r--r--src/engine/e_console.h64
-rw-r--r--src/engine/e_engine.c4
-rw-r--r--src/engine/e_system.c13
-rw-r--r--src/game/client/gc_client.cpp1
-rw-r--r--src/game/client/gc_console.cpp136
-rw-r--r--src/game/client/gc_console.h10
-rw-r--r--src/game/client/gc_hooks.cpp12
-rw-r--r--src/game/g_variables.h1
11 files changed, 613 insertions, 3 deletions
diff --git a/src/engine/e_config.c b/src/engine/e_config.c
index a4c47ec1..44008b77 100644
--- a/src/engine/e_config.c
+++ b/src/engine/e_config.c
@@ -7,7 +7,6 @@
 #include "e_system.h"
 #include "e_config.h"
 
-
 /* buffered stream for reading lines, should perhaps be something smaller */
 typedef struct
 {
diff --git a/src/engine/e_config.h b/src/engine/e_config.h
index 63933660..b6fbc93d 100644
--- a/src/engine/e_config.h
+++ b/src/engine/e_config.h
@@ -17,11 +17,17 @@ typedef struct
 
 extern CONFIGURATION config;
 
+void config_init();
 void config_set(const char *line);
 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);
+
 #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);
 #include "e_config_variables.h"
diff --git a/src/engine/e_console.c b/src/engine/e_console.c
new file mode 100644
index 00000000..edcd90fd
--- /dev/null
+++ b/src/engine/e_console.c
@@ -0,0 +1,368 @@
+#include "e_console.h"
+#include "e_config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+enum 
+{ 
+    STATE_START, 
+    STATE_INT, 
+    STATE_FLOAT, 
+    STATE_POT_FLOAT, 
+    STATE_STRING, 
+    STATE_QUOTED, 
+    STATE_ESCAPE 
+}; 
+
+static const char *store_string(struct lexer_result *res, const char *str, int len)
+{
+	const char *ptr = res->next_string;
+
+	memcpy(res->next_string, str, len);
+	res->next_string[len] = 0;
+	res->next_string += len+1;
+
+	return ptr;
+}
+
+static void save_token(struct 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]; 
+	tok->stored_string = store_string(res, *start, end - *start);
+    tok->type = type; 
+    ++res->num_tokens; 
+ 
+    *start = end + 1; 
+    *state = STATE_START; 
+    ++*index; 
+} 
+ 
+int digit(char c) 
+{ 
+    return '0' <= c && c <= '9'; 
+} 
+ 
+int lex(const char *line, struct 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));
+	res->next_string = res->string_storage;
+ 
+    for (c = start = line; *c != '\0' && res->num_tokens < MAX_TOKENS && length_left; ++c, --length_left) 
+    { 
+        /* printf("State: %d\n", state); */
+        switch (state) 
+        { 
+        case STATE_START: 
+            if (*c == ' ') 
+                start = c + 1; 
+            else if (digit(*c)) 
+                state = STATE_INT; 
+            else if (*c == '.') 
+                state = STATE_POT_FLOAT; 
+            else 
+                state = STATE_STRING; 
+            break; 
+ 
+        case STATE_INT: 
+            if (digit(*c)) 
+                ; 
+            else if (*c == '.') 
+                state = STATE_FLOAT; 
+            else if (*c == ' ') 
+                save_token(res, &i, &start, c, &state, TOKEN_INT); 
+            else 
+                state = STATE_STRING; 
+            break; 
+ 
+        case STATE_FLOAT: 
+            if (digit(*c)) 
+                ; 
+            else if (*c == ' ') 
+                save_token(res, &i, &start, c, &state, TOKEN_FLOAT); 
+            else 
+                state = STATE_STRING; 
+            break; 
+ 
+        case STATE_POT_FLOAT: 
+            if (digit(*c)) 
+                state = STATE_FLOAT; 
+            else if (*c == ' ') 
+                save_token(res, &i, &start, c, &state, TOKEN_STRING); 
+            else 
+                state = STATE_STRING; 
+            break; 
+ 
+        case STATE_STRING: 
+            if (*c == ' ') 
+                save_token(res, &i, &start, c, &state, TOKEN_STRING); 
+            break; 
+ 
+        case STATE_QUOTED: 
+            if (*c == '"') 
+                save_token(res, &i, &start, c, &state, TOKEN_STRING); 
+            else if (*c == '\\') 
+                state = STATE_ESCAPE; 
+            break; 
+ 
+        case STATE_ESCAPE: 
+            if (*c != ' ') 
+                state = STATE_QUOTED; 
+            break; 
+        } 
+    } 
+ 
+    switch (state) 
+    { 
+    case STATE_INT: 
+        save_token(res, &i, &start, c, &state, TOKEN_INT); 
+        break; 
+    case STATE_FLOAT: 
+        save_token(res, &i, &start, c, &state, TOKEN_FLOAT); 
+        break; 
+    case STATE_STRING: 
+    case STATE_QUOTED: 
+    case STATE_POT_FLOAT: 
+        save_token(res, &i, &start, c, &state, TOKEN_STRING); 
+        break; 
+    case STATE_ESCAPE: 
+        puts("LOL MALFORMED"); 
+        break; 
+    default: 
+        break; 
+    } 
+
+	return 0;
+}
+
+int extract_result_string(struct lexer_result *result, int index, const char **str)
+{
+	if (index < 0 || index >= result->num_tokens)
+		return -1;
+	else
+	{
+		struct token *t = &result->tokens[index];
+
+		*str = t->stored_string;
+
+		return 0;
+	}
+}
+
+int extract_result_int(struct lexer_result *result, int index, int *i)
+{
+	if (index < 0 || index >= result->num_tokens)
+		return -1;
+	else
+	{
+		struct token *t = &result->tokens[index];
+		const char *str;
+
+		if (t->type != TOKEN_INT)
+			return -2;
+
+		extract_result_string(result, index, &str);
+
+		*i = atoi(str);
+
+		return 0;
+	}
+}
+
+int extract_result_float(struct lexer_result *result, int index, float *f)
+{
+	if (index < 0 || index >= result->num_tokens)
+		return -1;
+	else
+	{
+		struct 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);
+
+		*f = atof(str);
+
+		return 0;
+	}
+}
+
+static COMMAND *first_command = 0x0;
+
+COMMAND *console_find_command(const char *name)
+{
+	COMMAND *cmd;
+	for (cmd = first_command; cmd; cmd = cmd->next)
+		if (strcmp(cmd->name, name) == 0)
+			return cmd;
+
+	return 0x0;
+}
+
+void console_register(COMMAND *cmd)
+{
+	cmd->next = first_command;
+	first_command = cmd;
+}
+
+
+static int console_validate(COMMAND *command, struct lexer_result *result)
+{
+	const char *c = command->params;
+	int i = 1;
+
+	const char *dummy_s;
+	int dummy_i;
+	float dummy_f;
+
+	while (*c && *c != '?')
+	{
+		switch (*c)
+		{
+		case 's':
+			if (extract_result_string(result, i, &dummy_s))
+				return -1;
+			break;
+		case 'i':
+			if (extract_result_int(result, i, &dummy_i))
+				return -1;
+			break;
+		case 'f':
+			if (extract_result_float(result, i, &dummy_f))
+				return -1;
+			break;
+		default:
+			// unknown char, so just continue...
+			c++;
+			continue;
+		}
+
+		i++;
+		c++;
+	}
+	
+	return 0;
+}
+
+static void (*print_callback)(const char *) = 0x0;
+
+void console_register_print_callback(void (*callback)(const char *))
+{
+	print_callback = callback;
+}
+
+void console_print(const char *str)
+{
+	if (print_callback)
+		print_callback(str);
+}
+
+void console_execute(const char *str)
+{
+	struct lexer_result result;
+	int error;
+
+	if ((error = lex(str, &result)))
+		printf("ERROR: %d\n", error);
+	else if (result.num_tokens > 0)
+	{
+		const char *name;
+		extract_result_string(&result, 0, &name);
+
+		COMMAND *command = console_find_command(name);
+
+		if (command)
+		{
+			if (console_validate(command, &result))
+			{
+				char buf[256];
+				sprintf(buf, "Invalid arguments... Usage: %s %s", command->name, command->params);
+				console_print(buf);
+			}
+			else
+				command->callback(&result, command->user_data);
+		}
+		else
+		{
+			char buf[256];
+			sprintf(buf, "No such command: %s.", name);
+			console_print(buf);
+		}
+	}
+}
+
+static void echo_command(struct lexer_result *result, void *user_data)
+{
+	const char *str;
+	extract_result_string(result, 1, &str);
+
+	console_print(str);
+}
+
+
+struct int_variable_data
+{
+	config_int_getter getter;
+	config_int_setter setter;
+};
+
+struct str_variable_data
+{
+	config_str_getter getter;
+	config_str_setter setter;
+};
+
+static void int_variable_command(struct lexer_result *result, void *user_data)
+{
+	struct int_variable_data *data = (struct int_variable_data *)user_data;
+	int new_val;
+
+	if (extract_result_int(result, 1, &new_val))
+	{
+		char buf[256];
+		sprintf(buf, "Value: %d", data->getter(&config));
+		console_print(buf);
+	}
+	else
+	{
+		data->setter(&config, new_val);
+	}
+}
+
+static void str_variable_command(struct lexer_result *result, void *user_data)
+{
+	struct str_variable_data *data = (struct str_variable_data *)user_data;
+	const char *new_val;
+
+	if (extract_result_string(result, 1, &new_val))
+	{
+		char buf[256];
+		sprintf(buf, "Value: %s", data->getter(&config));
+		console_print(buf);
+	}
+	else
+	{
+		data->setter(&config, new_val);
+	}
+}
+
+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) }
+
+    #include "e_config_variables.h" 
+
+    #undef MACRO_CONFIG_INT 
+    #undef MACRO_CONFIG_STR 
+}
diff --git a/src/engine/e_console.h b/src/engine/e_console.h
new file mode 100644
index 00000000..c383e8b5
--- /dev/null
+++ b/src/engine/e_console.h
@@ -0,0 +1,64 @@
+#ifndef _CONSOLE_H
+#define _CONSOLE_H
+
+#ifdef __cplusplus
+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 lex(const char *line, struct lexer_result *result);
+
+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
+{
+	const char *name;
+	const char *params;
+	console_callback callback;
+	void *user_data;
+	struct COMMAND *next;
+	
+} COMMAND;
+
+void console_init();
+void console_register(COMMAND *cmd);
+void console_execute(const char *str);
+void console_print(const char *str);
+void console_register_print_callback(void (*callback)(const char *));
+
+#define MACRO_REGISTER_COMMAND(name, params, func, ptr) { static COMMAND cmd = { name, params, func, ptr, 0x0 }; console_register(&cmd); }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c
index dc6484d6..06b82f7f 100644
--- a/src/engine/e_engine.c
+++ b/src/engine/e_engine.c
@@ -5,6 +5,7 @@
 #include <engine/e_system.h>
 #include <engine/e_interface.h>
 #include <engine/e_config.h>
+#include <engine/e_console.h>
 
 static char application_save_path[512] = {0};
 
@@ -39,6 +40,9 @@ void engine_init(const char *appname, int argc, char **argv)
 			fs_makedir(path);
 		}
 	}
+
+	/* init console */
+	console_init();
 	
 	/* reset the config */
 	config_reset();
diff --git a/src/engine/e_system.c b/src/engine/e_system.c
index bf445c9c..e9c96fa3 100644
--- a/src/engine/e_system.c
+++ b/src/engine/e_system.c
@@ -8,6 +8,7 @@
 
 #include "e_detect.h"
 #include "e_system.h"
+#include "e_console.h"
 
 #if defined(CONF_FAMILY_UNIX)
 	#include <sys/time.h>
@@ -88,6 +89,18 @@ void dbg_msg(const char *sys, const char *fmt, ...)
 	va_end(args);
 	printf("\n");
 	fflush(stdout);
+
+	{
+		char str[2048];
+
+		sprintf(str, "[%s]: ", sys);
+
+		va_start(args, fmt);
+		vsprintf(str+strlen(str), fmt, args);
+		va_end(args);
+
+		console_print(str);
+	}
 }
 
 int memory_alloced = 0;
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index e460abe0..d72efe8e 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -20,6 +20,7 @@ extern "C" {
 #include "gc_client.h"
 #include "gc_render.h"
 #include "gc_anim.h"
+#include "gc_console.h"
 
 struct data_container *data = 0;
 static int64 debug_firedelay = 0;
diff --git a/src/game/client/gc_console.cpp b/src/game/client/gc_console.cpp
new file mode 100644
index 00000000..277cb675
--- /dev/null
+++ b/src/game/client/gc_console.cpp
@@ -0,0 +1,136 @@
+#include "gc_console.h"
+
+extern "C" {
+	#include <engine/e_system.h>
+	#include <engine/e_interface.h>
+	#include <engine/e_config.h>
+	#include <engine/e_console.h>
+	#include <engine/client/ec_font.h>
+}
+
+#include <cstring>
+#include <cstdio>
+
+#include "gc_ui.h"
+
+static unsigned int console_input_len = 0;
+static char console_input[256] = {0};
+static int active = 0;
+
+static char backlog[256][256] = {0};
+static int backlog_len;
+
+static void client_console_print(const char *str)
+{
+	int len = strlen(str);
+
+	if (len > 255)
+		len = 255;
+
+	if (backlog_len >= 256)
+	{
+		puts("console backlog full");
+	}
+
+	memcpy(backlog[backlog_len], str, len);
+	backlog[backlog_len][len] = 0;
+
+	backlog_len++;
+
+	//dbg_msg("console", "FROM CLIENT!! %s", str);
+}
+
+void client_console_init()
+{
+	console_register_print_callback(client_console_print);
+}
+
+void console_handle_input()
+{
+	for(int i = 0; i < inp_num_events(); i++)
+	{
+		INPUTEVENT e = inp_get_event(i);
+
+		if (e.key == KEY_F3)
+		{
+			console_toggle();
+		}
+
+		if (active)
+		{
+			if (!(e.ch >= 0 && e.ch < 32))
+			{
+				if (console_input_len < sizeof(console_input) - 1)
+				{
+					console_input[console_input_len] = e.ch;
+					console_input[console_input_len+1] = 0;
+					console_input_len++;
+				}
+			}
+
+			if(e.key == KEY_BACKSPACE)
+			{
+				if(console_input_len > 0)
+				{
+					console_input[console_input_len-1] = 0;
+					console_input_len--;
+				}
+			}
+			else if(e.key == KEY_ENTER)
+			{
+				console_execute(console_input);
+				console_input[0] = 0;
+				console_input_len = 0;
+			}
+		}
+	}
+
+	if (active)
+		inp_clear_events();
+}
+
+void console_toggle()
+{
+	active ^= 1;
+}
+
+void console_render()
+{
+    RECT screen = *ui_screen();
+	float console_height = screen.h*3/5.0f;
+	gfx_mapscreen(screen.x, screen.y, screen.w, screen.h);
+
+    gfx_texture_set(-1);
+    gfx_quads_begin();
+    gfx_setcolor(0.4,0.2,0.2,0.8);
+    gfx_quads_drawTL(0,0,screen.w,console_height);
+    gfx_quads_end();
+
+	{
+		float font_size = 12.0f;
+		float row_spacing = font_size*1.4f;
+		float width = gfx_text_width(0, 12, console_input, -1);
+		float x = 3, y = console_height - row_spacing - 2;
+		int backlog_index = backlog_len-1;
+
+		gfx_text(0, x, y, font_size, console_input, -1);
+		gfx_text(0, x+width+1, y, font_size, "_", -1);
+
+		y -= row_spacing;
+
+		while (y > 0.0f && backlog_index >= 0)
+		{
+			const char *line = backlog[backlog_index];
+			gfx_text(0, x, y, font_size, line, -1);
+
+			backlog_index--;
+			y -= row_spacing;
+		}
+	}
+}
+
+int console_active()
+{
+	return active;
+}
+
diff --git a/src/game/client/gc_console.h b/src/game/client/gc_console.h
new file mode 100644
index 00000000..87628a2a
--- /dev/null
+++ b/src/game/client/gc_console.h
@@ -0,0 +1,10 @@
+#ifndef _GC_CONSOLE_H
+#define _GC_CONSOLE_H
+
+void console_handle_input();
+void console_toggle();
+void console_render();
+int console_active();
+void client_console_init();
+
+#endif
diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp
index a58b554a..d70e84a5 100644
--- a/src/game/client/gc_hooks.cpp
+++ b/src/game/client/gc_hooks.cpp
@@ -17,6 +17,7 @@ extern "C" {
 #include "gc_skin.h"
 #include "gc_render.h"
 #include "gc_map_image.h"
+#include "gc_console.h"
 
 extern unsigned char internal_data[];
 
@@ -36,6 +37,8 @@ extern "C" void modc_init()
 	gfx_text_set_default_font(&default_font);
 
 	menu_init();
+
+	client_console_init();
 	
 	// setup sound channels
 	snd_set_channel(CHN_GUI, 1.0f, 0.0f);
@@ -288,10 +291,10 @@ extern "C" void modc_newsnapshot()
 		client_datas[i].update_render_info();
 }
 
-
-
 extern "C" void modc_render()
 {
+	console_handle_input();
+
 	// this should be moved around abit
 	if(client_state() == CLIENTSTATE_ONLINE)
 	{
@@ -310,6 +313,11 @@ extern "C" void modc_render()
 	else // if (client_state() != CLIENTSTATE_CONNECTING && client_state() != CLIENTSTATE_LOADING)
 	{
 		menu_render();
+		if (console_active())
+		{
+			console_render();
+			return;
+		}
 	}
 
 	//
diff --git a/src/game/g_variables.h b/src/game/g_variables.h
index 6a9fb35d..08abe313 100644
--- a/src/game/g_variables.h
+++ b/src/game/g_variables.h
@@ -23,6 +23,7 @@ MACRO_CONFIG_INT(key_teamchat, 'Y', 32, 512)
 MACRO_CONFIG_INT(key_console, 256+2, 32, 512)
 MACRO_CONFIG_INT(key_remoteconsole, 256+3, 32, 512)
 
+MACRO_CONFIG_INT(key_toggleconsole, 256+4, 32, 512)
 
 MACRO_CONFIG_INT(dbg_bots, 0, 0, 11)