diff -Nru teeworlds-0.5.1-src/src/engine/e_config.h gameop-tw/src/engine/e_config.h
--- teeworlds-0.5.1-src/src/engine/e_config.h	2009-01-25 13:50:32.000000000 +0100
+++ gameop-tw/src/engine/e_config.h	2009-10-06 14:50:45.000000000 +0100
@@ -25,7 +25,8 @@
 {
 	CFGFLAG_SAVE=1,
 	CFGFLAG_CLIENT=2,
-	CFGFLAG_SERVER=4
+	CFGFLAG_SERVER=4,
+	CFGFLAG_GAMEOP=8
 };
 
 typedef int (*CONFIG_INT_GETTER)(CONFIGURATION *c);
diff -Nru teeworlds-0.5.1-src/src/engine/e_config_variables.h gameop-tw/src/engine/e_config_variables.h
--- teeworlds-0.5.1-src/src/engine/e_config_variables.h	2009-01-25 13:50:32.000000000 +0100
+++ gameop-tw/src/engine/e_config_variables.h	2009-10-06 14:49:10.000000000 +0100
@@ -64,7 +64,8 @@
 MACRO_CONFIG_INT(sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients that are allowed on a server")
 MACRO_CONFIG_INT(sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
 MACRO_CONFIG_INT(sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing")
-MACRO_CONFIG_STR(sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password")
+MACRO_CONFIG_STR(sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password (full access)")
+MACRO_CONFIG_STR(sv_gameop_password, 32, "", CFGFLAG_SERVER, "GameOp console password (limited access)")
 MACRO_CONFIG_INT(sv_map_reload, 0, 0, 1, CFGFLAG_SERVER, "Reload the current map")
 
 MACRO_CONFIG_INT(debug, 0, 0, 1, CFGFLAG_CLIENT|CFGFLAG_SERVER, "Debug mode")
diff -Nru teeworlds-0.5.1-src/src/engine/e_console.c gameop-tw/src/engine/e_console.c
--- teeworlds-0.5.1-src/src/engine/e_console.c	2009-01-25 13:50:32.000000000 +0100
+++ gameop-tw/src/engine/e_console.c	2009-10-06 14:53:15.000000000 +0100
@@ -215,7 +215,7 @@
 		print_callback(str, print_callback_userdata);
 }
 
-void console_execute_line_stroked(int stroke, const char *str)
+void console_execute_line_stroked_gameop(int stroke, const char *str, const int gameop)
 {
 	PARSE_RESULT result;
 	COMMAND *command;
@@ -260,25 +260,34 @@
 
 		if(command)
 		{
-			int is_stroke_command = 0;
-			if(result.command[0] == '+')
-			{
-				/* insert the stroke direction token */
-				result.args[result.num_args] = strokestr;
-				result.num_args++;
-				is_stroke_command = 1;
-			}
-			
-			if(stroke || is_stroke_command)
+			if(!(command->flags & CFGFLAG_SERVER) || !gameop || command->flags & CFGFLAG_GAMEOP)
 			{
-				if(console_parse_args(&result, command->params))
+				int is_stroke_command = 0;
+				if(result.command[0] == '+')
 				{
-					char buf[256];
-					str_format(buf, sizeof(buf), "Invalid arguments... Usage: %s %s", command->name, command->params);
-					console_print(buf);
+					/* insert the stroke direction token */
+					result.args[result.num_args] = strokestr;
+					result.num_args++;
+					is_stroke_command = 1;
+				}
+				
+				if(stroke || is_stroke_command)
+				{
+					if(console_parse_args(&result, command->params))
+					{
+						char buf[256];
+						str_format(buf, sizeof(buf), "Invalid arguments... Usage: %s %s", command->name, command->params);
+						console_print(buf);
+					}
+					else
+						command->callback(&result, command->user_data);
 				}
-				else
-					command->callback(&result, command->user_data);
+			}
+			else
+			{
+				char buf[256];
+				str_format(buf, sizeof(buf), "Command is disabled for gameops: %s.", command->name);
+				console_print(buf);
 			}
 		}
 		else
@@ -291,6 +300,10 @@
 		str = next_part;
 	}
 }
+void console_execute_line_stroked(int stroke, const char *str)
+{
+	console_execute_line_stroked_gameop(stroke, str, 0);
+}
 
 void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user)
 {
@@ -387,6 +400,33 @@
 
 }
 
+static void con_gameop(void *result, void *user_data)
+{
+	COMMAND *cmd;
+	const char *string = console_arg_string(result, 0);
+	char buf[256];
+	for (cmd = first_command; cmd; cmd = cmd->next)
+	{
+		if(cmd->flags & CFGFLAG_SERVER && !str_comp_nocase(cmd->name, string))
+		{
+			if(console_arg_num(result) == 2)
+			{
+				if(console_arg_int(result, 1))
+					cmd->flags |= CFGFLAG_GAMEOP;
+				else
+					cmd->flags &= ~CFGFLAG_GAMEOP;
+			}
+			else
+			{
+				str_format(buf, sizeof(buf), "GameOp access: %s", cmd->flags & CFGFLAG_GAMEOP ? "enabled" : "disabled");
+				console_print(buf);
+			}
+			return;
+		}
+	}
+	str_format(buf, sizeof(buf), "No such command: %s.", string);
+	console_print(buf);
+}
 
 typedef struct 
 {
@@ -432,6 +472,7 @@
 {
 	MACRO_REGISTER_COMMAND("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_echo, 0x0, "Echo the text");
 	MACRO_REGISTER_COMMAND("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, con_exec, 0x0, "Execute the specified file");
+	MACRO_REGISTER_COMMAND("gameop", "s?i", CFGFLAG_SERVER, con_gameop, 0x0, "Enables the command for gameops");
 
 	#define MACRO_CONFIG_INT(name,def,min,max,flags,desc) { static INT_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?i", flags, int_variable_command, &data, desc) }
 	#define MACRO_CONFIG_STR(name,len,def,flags,desc) { static STR_VARIABLE_DATA data = { &config_get_ ## name, &config_set_ ## name }; MACRO_REGISTER_COMMAND(#name, "?r", flags, str_variable_command, &data, desc) }
diff -Nru teeworlds-0.5.1-src/src/engine/e_console.h gameop-tw/src/engine/e_console.h
--- teeworlds-0.5.1-src/src/engine/e_console.h	2009-01-25 13:50:32.000000000 +0100
+++ gameop-tw/src/engine/e_console.h	2009-10-06 14:51:34.000000000 +0100
@@ -22,6 +22,7 @@
 void console_register(COMMAND *cmd);
 void console_execute_line(const char *str);
 void console_execute_line_stroked(int stroke, const char *str);
+void console_execute_line_stroked_gameop(int stroke, const char *str, const int gameop);
 void console_execute_file(const char *filename);
 void console_possible_commands(const char *str, int flagmask, void (*callback)(const char *cmd, void *user), void *user);
 COMMAND *console_get_command(const char *cmd);
diff -Nru teeworlds-0.5.1-src/src/engine/server/es_server.c gameop-tw/src/engine/server/es_server.c
--- teeworlds-0.5.1-src/src/engine/server/es_server.c	2009-01-25 13:50:32.000000000 +0100
+++ gameop-tw/src/engine/server/es_server.c	2009-10-06 14:52:48.000000000 +0100
@@ -77,9 +77,15 @@
 	
 	SRVCLIENT_SNAPRATE_INIT=0,
 	SRVCLIENT_SNAPRATE_FULL,
-	SRVCLIENT_SNAPRATE_RECOVER
+	SRVCLIENT_SNAPRATE_RECOVER,
+
+	SRVCLIENT_AUTHED_NOT = 0,
+	SRVCLIENT_AUTHED_GAMEOP,
+	SRVCLIENT_AUTHED_ADMIN
 };
 
+static int rcon_state = SRVCLIENT_AUTHED_GAMEOP;
+
 typedef struct 
 {
 	int data[MAX_INPUT_SIZE];
@@ -582,7 +588,7 @@
 	clients[cid].state = SRVCLIENT_STATE_AUTH;
 	clients[cid].name[0] = 0;
 	clients[cid].clan[0] = 0;
-	clients[cid].authed = 0;
+	clients[cid].authed = SRVCLIENT_AUTHED_NOT;
 	reset_client(cid);
 	return 0;
 }
@@ -596,7 +602,7 @@
 	clients[cid].state = SRVCLIENT_STATE_EMPTY;
 	clients[cid].name[0] = 0;
 	clients[cid].clan[0] = 0;
-	clients[cid].authed = 0;
+	clients[cid].authed = SRVCLIENT_AUTHED_NOT;
 	snapstorage_purge_all(&clients[cid].snapshots);
 	return 0;
 }
@@ -628,7 +634,7 @@
 	
 	for(i = 0; i < MAX_CLIENTS; i++)
 	{
-		if(clients[i].state != SRVCLIENT_STATE_EMPTY && clients[i].authed)
+		if(clients[i].state != SRVCLIENT_STATE_EMPTY && clients[i].authed >= rcon_state)
 			server_send_rcon_line(i, line);
 	}
 	
@@ -794,10 +800,13 @@
 			{
 				const char *cmd = msg_unpack_string();
 				
-				if(msg_unpack_error() == 0 && clients[cid].authed)
+				if(msg_unpack_error() == 0 && clients[cid].authed > SRVCLIENT_AUTHED_NOT)
 				{
-					dbg_msg("server", "cid=%d rcon='%s'", cid, cmd);
-					console_execute_line(cmd);
+					if(clients[cid].authed == SRVCLIENT_AUTHED_ADMIN)
+						rcon_state = SRVCLIENT_AUTHED_ADMIN;
+					dbg_msg("server", "cid=%d %s='%s'", cid, clients[cid].authed == SRVCLIENT_AUTHED_ADMIN ? "rcon" : "gameop", cmd);
+					console_execute_line_stroked_gameop(1, cmd, clients[cid].authed == SRVCLIENT_AUTHED_GAMEOP);
+					rcon_state = SRVCLIENT_AUTHED_GAMEOP;
 				}
 			}
 			else if(msg == NETMSG_RCON_AUTH)
@@ -808,21 +817,32 @@
 				
 				if(msg_unpack_error() == 0)
 				{
-					if(config.sv_rcon_password[0] == 0)
+					if(config.sv_rcon_password[0] == 0 && config.sv_gameop_password[0] == 0)
 					{
-						server_send_rcon_line(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
+						server_send_rcon_line(cid, "No rcon password set on server. Set sv_rcon_password of sv_gameop_password to enable the remote console.");
 					}
-					else if(strcmp(pw, config.sv_rcon_password) == 0)
+					else if(clients[cid].authed != SRVCLIENT_AUTHED_ADMIN && config.sv_rcon_password[0] && strcmp(pw, config.sv_rcon_password) == 0)
 					{
 						msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL);
 						msg_pack_int(1);
 						msg_pack_end();
 						server_send_msg(cid);
 						
-						clients[cid].authed = 1;
+						clients[cid].authed = SRVCLIENT_AUTHED_ADMIN;
 						server_send_rcon_line(cid, "Authentication successful. Remote console access granted.");
 						dbg_msg("server", "cid=%d authed", cid);
 					}
+					else if(clients[cid].authed != SRVCLIENT_AUTHED_GAMEOP && config.sv_gameop_password[0] && strcmp(pw, config.sv_gameop_password) == 0)
+					{
+						msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL);
+						msg_pack_int(1);
+						msg_pack_end();
+						server_send_msg(cid);
+						
+						clients[cid].authed = SRVCLIENT_AUTHED_GAMEOP;
+						server_send_rcon_line(cid, "Authentication successful. GameOp console access granted.");
+						dbg_msg("server", "cid=%d authed gameop", cid);
+					}
 					else
 					{
 						server_send_rcon_line(cid, "Wrong password.");
@@ -1212,7 +1232,11 @@
 
 static void con_kick(void *result, void *user_data)
 {
-	server_kick(console_arg_int(result, 0), "kicked by console");
+	const int cid = console_arg_int(result, 0);
+	if(rcon_state >= clients[cid].authed)
+		server_kick(cid, "kicked by console");
+	else
+		dbg_msg("server", "kick command denied");
 }
 
 static int str_allnum(const char *str)
@@ -1248,6 +1272,11 @@
 			dbg_msg("server", "invalid client id");
 			return;
 		}
+		else if(rcon_state < clients[cid].authed)
+		{
+			dbg_msg("server", "ban command denied");
+			return;
+		}
 
 		netserver_client_addr(net, cid, &addr);
 		server_ban_add(addr, minutes*60);
