Browse Source

Merge branch 'console'

mikymod 12 năm trước cách đây
mục cha
commit
0ef8bb517f

+ 2 - 0
engine/Android.mk

@@ -110,6 +110,7 @@ LOCAL_SRC_FILES :=\
 	TextureResource.cpp\
 	VertexShaderResource.cpp\
 	Game.cpp\
+	ConsoleServer.cpp\
 \
 
 LOCAL_C_INCLUDES	:=\
@@ -129,6 +130,7 @@ LOCAL_C_INCLUDES	:=\
 	$(LOCAL_PATH)/network\
 	$(LOCAL_PATH)/os\
 	$(LOCAL_PATH)/os/android\
+	$(LOCAL_PATH)/os/posix\
 	$(LOCAL_PATH)/renderers\
 	$(LOCAL_PATH)/renderers/gles\
 	$(LOCAL_PATH)/renderers/gles/egl\

+ 3 - 2
engine/CMakeLists.txt

@@ -70,8 +70,8 @@ set (SRC
 	ArchiveBundle.cpp
 	FileBundle.cpp
 
-	Game.cpp
 	JSONParser.cpp
+	ConsoleServer.cpp
 
 	FPSSystem.cpp
 )
@@ -95,8 +95,9 @@ set (HEADERS
 	ArchiveBundle.h
 	FileBundle.h
 
-	Game.h
 	JSONParser.h
+	ConsoleServer.h
+
 	FPSSystem.h
 )
 

+ 130 - 0
engine/ConsoleServer.cpp

@@ -0,0 +1,130 @@
+/*
+Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "ConsoleServer.h"
+#include "Log.h"
+#include "StringUtils.h"
+#include "Device.h"
+#include "LuaEnvironment.h"
+#include "StringUtils.h"
+#include "JSONParser.h"
+#include "IntSetting.h"
+
+namespace crown
+{
+
+static IntSetting g_port("read_port", "port used for reading", 10000, 9999, 65535);
+
+//-----------------------------------------------------------------------------
+ConsoleServer::ConsoleServer() :
+	m_active(false),
+	m_thread(ConsoleServer::background_thread, (void*)this, "console-thread")
+{
+	string::strncpy(m_cmd_buffer, "", 1024);
+	string::strncpy(m_err_buffer, "", 1024);
+}
+
+//-----------------------------------------------------------------------------
+void ConsoleServer::init()
+{
+	LuaEnvironment* lua = device()->lua_environment();
+
+	m_active = true;
+}
+
+//-----------------------------------------------------------------------------
+void ConsoleServer::shutdown()
+{
+	m_active = false;
+
+	m_socket.close();
+}
+
+//-----------------------------------------------------------------------------
+void ConsoleServer::read_eval_loop()
+{
+	m_socket.open(g_port);
+	
+	char cmd[1024];
+
+	while (m_active)
+	{
+		string::strncpy(cmd, "", 1024);
+		receive((char*)cmd, 1024);
+
+		// Fill command buffer
+		string::strncpy(m_cmd_buffer, cmd, 1024);
+	}
+
+	Log::i("End read-eval loop");
+	m_socket.close();
+}
+
+//-----------------------------------------------------------------------------
+void ConsoleServer::execute()
+{
+	m_command_mutex.lock();
+
+	LuaEnvironment* lua = device()->lua_environment();
+
+	lua->load_buffer(m_cmd_buffer, string::strlen(m_cmd_buffer));
+	lua->execute(0, 0);
+
+	string::strncpy(m_cmd_buffer, "", 1024);
+
+	string::strncpy(m_err_buffer, lua->error(),  1024);
+
+	if (string::strcmp(m_err_buffer, "") != 0)
+	{
+		// Fill error buffer
+		send((char*)m_err_buffer, 1024);
+	}
+
+	string::strncpy(m_err_buffer, "", 1024);
+
+	m_command_mutex.unlock();
+}
+
+//-----------------------------------------------------------------------------
+void ConsoleServer::send(const void* data, size_t size)
+{
+	bool sent = m_socket.send(data, size);
+}
+
+//-----------------------------------------------------------------------------
+void ConsoleServer::receive(char* data, size_t size)
+{
+	int32_t bytes_read = m_socket.receive(data, size);
+}
+
+//-----------------------------------------------------------------------------
+void* ConsoleServer::background_thread(void* thiz)
+{
+	((ConsoleServer*)thiz)->read_eval_loop();	
+}
+
+
+} // namespace crown

+ 42 - 14
engine/Game.h → engine/ConsoleServer.h

@@ -26,23 +26,51 @@ OTHER DEALINGS IN THE SOFTWARE.
 
 #pragma once
 
+#include "Types.h"
+#include "TCPSocket.h"
+#include "Thread.h"
+#include "Mutex.h"
+
 namespace crown
 {
 
-/// Called exactly once after the engine is fully initialized
-/// and ready to use. This function is the right place to allocate
-/// and initialize all the main components of the game.
-void init();
+class ConsoleServer
+{
+public:
+
+	/// Constructor
+							ConsoleServer();
+	/// Start listening on @port
+	void					init();
+	/// Stop listening
+	void					shutdown();
+	/// Read-evaluation loop, executed on a different thread
+	void					read_eval_loop();
+	/// Execute commands, executed on main thread
+	void					execute();
+	/// Send data to client
+	void					send(const void* data, size_t size = 1024);
+	/// Receive data to client
+	void					receive(char* data, size_t size = 1024);
+
+private:
+
+	static void*			background_thread(void* thiz);
+
+private:
+
+	os::TCPSocket			m_socket;
+
+	os::Thread				m_thread;
+	os::Mutex				m_command_mutex;
+
+	// Is console active?
+	bool					m_active;
+	// Commands buffer
+	char					m_cmd_buffer[1024];
 
-/// Called just before the engine starts to deallocate resources and
-/// subsystems leading to terminating the execution.
-/// Here you can safely perform all the necessary deallocation/destruction
-/// of the previously allocated game resources and/or systems. 
-void shutdown();
+	char 					m_err_buffer[1024];
 
-/// Called once per frame, here is the place you tipically perform input checking,
-/// updates, drawing and so on. The @a dt parameter contains the last frame delta time
-/// in seconds. 
-void frame(float dt);
+};
 
-}
+} // namespace crown

+ 60 - 6
engine/Device.cpp

@@ -49,7 +49,8 @@ OTHER DEALINGS IN THE SOFTWARE.
 #include "JSONParser.h"
 #include "DiskFile.h"
 #include "Memory.h"
-#include "Game.h"
+#include "LuaEnvironment.h"
+#include "ConsoleServer.h"
 
 namespace crown
 {
@@ -76,13 +77,14 @@ Device::Device() :
 
 	m_filesystem(NULL),
 	m_input_manager(NULL),
+	m_lua_environment(NULL),
 	m_renderer(NULL),
 	m_debug_renderer(NULL),
 
 	m_resource_manager(NULL),
 	m_resource_bundle(NULL),
 
-	m_game_library(NULL)
+	m_console_server(NULL)
 {
 	// Select executable dir by default
 	string::strncpy(m_preferred_root_path, os::get_cwd(), MAX_PATH_LENGTH);
@@ -120,6 +122,10 @@ bool Device::init(int argc, char** argv)
 
 	create_debug_renderer();
 
+	create_lua_environment();
+
+	create_console_server();
+
 	read_engine_settings();
 
 	Log::i("Crown Engine initialized.");
@@ -127,7 +133,7 @@ bool Device::init(int argc, char** argv)
 	Log::i("Initializing Game...");
 
 	// Initialize the game through init game function
-	crown::init();
+	m_lua_environment->game_init();
 
 	m_is_init = true;
 
@@ -151,8 +157,25 @@ void Device::shutdown()
 	}
 
 	// Shutdowns the game
-	crown::shutdown();
-	
+	m_lua_environment->game_shutdown();
+
+	Log::i("Releasing ConsoleServer...");
+	if (m_console_server)
+	{
+		m_console_server->shutdown();
+
+		CE_DELETE(m_allocator, m_console_server);
+	}
+
+	Log::i("Releasing LuaEnvironment...");
+	if (m_lua_environment)
+	{
+		m_lua_environment->shutdown();
+		
+		CE_DELETE(m_allocator, m_lua_environment);
+	}
+
+	Log::i("Releasing InputManager...");
 	if (m_input_manager)
 	{
 		CE_DELETE(m_allocator, m_input_manager);
@@ -224,6 +247,12 @@ InputManager* Device::input_manager()
 	return m_input_manager;
 }
 
+//-----------------------------------------------------------------------------
+LuaEnvironment* Device::lua_environment()
+{
+	return m_lua_environment;
+}
+
 //-----------------------------------------------------------------------------
 OsWindow* Device::window()
 {
@@ -266,6 +295,10 @@ Accelerometer* Device::accelerometer()
 	return m_input_manager->accelerometer();
 }
 
+ConsoleServer* Device::console_server()
+{
+	return m_console_server;
+}
 //-----------------------------------------------------------------------------
 void Device::start()
 {
@@ -323,7 +356,9 @@ void Device::frame()
 	m_window->frame();
 	m_input_manager->frame(frame_count());
 
-	crown::frame(last_delta_time());
+	m_lua_environment->game_frame(last_delta_time());
+
+	m_console_server->execute();
 
 	m_debug_renderer->draw_all();
 	m_renderer->frame();
@@ -430,6 +465,25 @@ void Device::create_debug_renderer()
 	Log::d("Debug renderer created.");
 }
 
+//-----------------------------------------------------------------------------
+void Device::create_lua_environment()
+{
+	m_lua_environment = CE_NEW(m_allocator, LuaEnvironment)();
+
+	m_lua_environment->init();
+
+	Log::d("Lua environment created.");
+}
+
+void Device::create_console_server()
+{
+	m_console_server = CE_NEW(m_allocator, ConsoleServer)();
+
+	m_console_server->init();
+
+	Log::d("Console server created.");
+}
+
 //-----------------------------------------------------------------------------
 void Device::parse_command_line(int argc, char** argv)
 {

+ 11 - 3
engine/Device.h

@@ -48,7 +48,8 @@ class Keyboard;
 class Mouse;
 class Touch;
 class Accelerometer;
-class Game;
+class LuaEnvironment;
+class ConsoleServer;
 
 /// The Engine.
 /// It is the place where to look for accessing all of
@@ -102,6 +103,7 @@ public:
 	Filesystem*				filesystem();
 	ResourceManager*		resource_manager();
 	InputManager*			input_manager();
+	LuaEnvironment*			lua_environment();
 
 	OsWindow*				window();
 	Renderer*				renderer();
@@ -112,16 +114,21 @@ public:
 	Touch*					touch();
 	Accelerometer*			accelerometer();
 
+	ConsoleServer*			console_server();
+
 private:
 
 	void					create_filesystem();
 	void					create_resource_manager();
 	void					create_input_manager();
+	void 					create_lua_environment();
 
 	void					create_window();
 	void					create_renderer();
 	void					create_debug_renderer();
 
+	void					create_console_server();
+
 	void					parse_command_line(int argc, char** argv);
 	void					check_preferred_settings();
 	void					read_engine_settings();
@@ -156,6 +163,7 @@ private:
 
 	OsWindow*				m_window;
 	InputManager*			m_input_manager;
+	LuaEnvironment*			m_lua_environment;
 	Renderer*				m_renderer;
 	DebugRenderer*			m_debug_renderer;
 
@@ -163,8 +171,8 @@ private:
 	ResourceManager*		m_resource_manager;
 	Bundle*					m_resource_bundle;
 
-	// The game currently running
-	void*					m_game_library;
+	// Debug subsystems
+	ConsoleServer*			m_console_server;
 
 private:
 

+ 0 - 49
engine/Game.cpp

@@ -1,49 +0,0 @@
-#include "Crown.h"
-#include "Game.h"
-#include "lua.hpp"
-
-namespace crown
-{
-
-StringSetting g_boot("boot_file", "lua main file", "lua/game.raw");
-
-lua_State* L;
-
-void init()
-{
-	L = luaL_newstate();
-	luaL_openlibs(L);
-
-	lua_cpcall(L, luaopen_libcrown, NULL);
-
-	const char* path = device()->filesystem()->os_path(g_boot.value());
-
-	if (luaL_loadfile(L, path) || lua_pcall(L, 0, 0, 0))
-	{
-		os::printf("error: %s", lua_tostring(L, -1));
-	}
-
-	lua_getglobal(L, "init");
-
-	lua_pcall(L, 0, 0, 0);
-}
-
-void shutdown()
-{
-	lua_getglobal(L, "shutdown");
-
-	lua_pcall(L, 0, 0, 0);
-
-	lua_close(L);
-}
-
-void frame(float dt)
-{
-	lua_getglobal(L, "frame");
-
-	lua_pushnumber(L, dt);
-
-	lua_pcall(L, 1, 0, 0);
-}
-
-}

+ 260 - 17
engine/lua/LuaEnvironment.cpp

@@ -24,17 +24,187 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
 */
 
+
+#include "Device.h"
+#include "OS.h"
+#include "Assert.h"
+#include "Log.h"
 #include "LuaEnvironment.h"
+#include "StringSetting.h"
+#include "Filesystem.h"
 
 namespace crown
 {
 
-LuaEnvironment::LuaEnvironment(lua_State* L) :
-	m_state(L)
+StringSetting g_boot("boot_file", "lua main file", "lua/game.raw");
+
+/*
+*N.B: Lua garbage collection is actually disabled
+*/
+
+//-----------------------------------------------------------------------------
+LuaEnvironment::LuaEnvironment() :
+	m_state(luaL_newstate()),
+	m_is_used(false)
+	//m_thread(LuaEnvironment::background_thread, (void*)this, "lua-environment-thread")
 {
+	// Open Lua default libraries
+	string::strncpy(m_error_buffer, "", 1024);
+
+	string::strncpy(m_tmp_buffer, "", 1024);
 }
 
-//-----------------------------------------------------------
+//-----------------------------------------------------------------------------
+void LuaEnvironment::init()
+{
+	// Open default libraries
+	luaL_openlibs(m_state);
+	// Open Crown library
+	lua_cpcall(m_state, luaopen_libcrown, NULL);
+
+	load_buffer(class_system, string::strlen(class_system));
+	execute(0, 0);
+	load_buffer(commands_list, string::strlen(commands_list));
+	execute(0, 0);
+	load_buffer(get_cmd_by_name, string::strlen(get_cmd_by_name));
+	execute(0, 0);
+	load_buffer(tmp_print_table, string::strlen(tmp_print_table));
+	execute(0, 0);
+	load_buffer(count_all, string::strlen(count_all));
+	execute(0, 0);
+
+	m_is_used = true;
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::shutdown()
+{
+	lua_close(m_state);
+
+	m_is_used = false;
+}
+
+//-----------------------------------------------------------------------------
+lua_State* LuaEnvironment::state()
+{
+	return m_state;
+}
+
+//-----------------------------------------------------------------------------
+const char* LuaEnvironment::error()
+{	
+	string::strncpy(m_tmp_buffer, m_error_buffer, 1024);
+
+	string::strncpy(m_error_buffer, "", 1024);
+
+	return m_tmp_buffer;
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::load_buffer(const char* buffer, size_t len)
+{
+	int32_t loaded = luaL_loadbuffer(m_state, buffer, len, "");
+
+	if (loaded != 0)
+	{
+		lua_error();
+	}
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::load_file(const char* file)
+{
+	int32_t loaded = luaL_loadfile(m_state, file);
+
+	if (loaded != 0)
+	{
+		lua_error();
+	}
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::load_string(const char* str)
+{
+	int32_t loaded = luaL_loadstring(m_state, str);
+
+	if (loaded != 0)
+	{
+		lua_error();
+	}
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::get_global_symbol(const char* symbol)
+{
+	lua_getglobal(m_state, symbol);
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::execute(int32_t args, int32_t results)
+{
+	int32_t executed = lua_pcall(m_state, args, results, 0);
+
+	if (executed != 0)
+	{
+		lua_error();
+	}
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::collect_garbage()
+{
+	uint64_t start = os::milliseconds();
+
+	while ((os::milliseconds() - start) < device()->last_delta_time() && !m_is_used)
+	{
+		lua_gc(m_state, LUA_GCSTEP, 0);
+	}
+}
+
+//-----------------------------------------------------------------------------
+void* LuaEnvironment::background_thread(void* thiz)
+{
+	((LuaEnvironment*)thiz)->collect_garbage();	
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::game_init()
+{
+	const char* path = device()->filesystem()->os_path(g_boot.value());
+
+	load_file(path);
+	execute(0, 0);
+
+	get_global_symbol("init");
+	execute(0, 0);
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::game_shutdown()
+{
+	get_global_symbol("shutdown");
+	execute(0, 0);
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::game_frame(float dt)
+{
+	LuaStack stack(m_state);
+
+	get_global_symbol("frame");
+	stack.push_float(dt);
+	execute(1, 0);
+}
+
+//-----------------------------------------------------------------------------
+void LuaEnvironment::lua_error()
+{
+	string::strncpy(m_error_buffer, "", 1024);
+
+	string::strncpy(m_error_buffer, lua_tostring(m_state, -1), 1024);
+}
+
+//-----------------------------------------------------------------------------
 void LuaEnvironment::load_module_function(const char* module, const char* name, const lua_CFunction func)
 {
 	luaL_Reg entry[2];
@@ -58,27 +228,100 @@ void LuaEnvironment::load_module_enum(const char* module, const char* name, uint
 //-----------------------------------------------------------------------------
 CE_EXPORT int32_t luaopen_libcrown(lua_State* L)
 {
-	LuaEnvironment env(L);
+	LuaEnvironment* env = device()->lua_environment();
 
-	load_int_setting(env);
-	load_float_setting(env);
-	load_string_setting(env);
+	load_int_setting(*env);
+	load_float_setting(*env);
+	load_string_setting(*env);
 
-	load_vec2(env);
-	load_vec3(env);
-	load_mat4(env);
-	load_quat(env);
-	load_math(env);
+	load_vec2(*env);
+	load_vec3(*env);
+	load_mat4(*env);
+	load_quat(*env);
+	load_math(*env);
 
-	load_mouse(env);
-	load_keyboard(env);
-	load_accelerometer(env);
+	load_mouse(*env);
+	load_keyboard(*env);
+	load_accelerometer(*env);
 
-	load_device(env);
+	load_device(*env);
 
-	load_window(env);
+	load_window(*env);
 
 	return 1;
 }
 
+const char* LuaEnvironment::class_system =  "function class(klass, super) "
+    										"	if not klass then "
+        									"		klass = {} "
+                							"		local meta = {} "
+        									"		meta.__call = function(self, ...) "
+            								"			local object = {} "
+            								"			setmetatable(object, klass) "
+            								"			if object.init then object:init(...) end "
+            								"			return object "
+       										"		end "
+        									"		setmetatable(klass, meta) "
+    										"	end "  
+    										"	if super then "
+        									"		for k,v in pairs(super) do "
+            								"			klass[k] = v "
+        									"		end "
+    										"	end "
+    										"	klass.__index = klass "
+    										"	return klass "
+											"end";
+
+
+const char* LuaEnvironment::commands_list = "function get_all_commands() "
+											"	local cmds = {}; "
+											"	for class_name,class in pairs(_G) do "
+											"		if type(class) == 'table' then "
+			 								"			for func_name,func in pairs(class) do "
+			 								"				if type(func) == 'function' then "
+											"					cmds[#cmds+1] = class_name .. '.' .. func_name "
+											"				end "
+											"			end "
+											"		end "
+											"	end "
+											"	return cmds "
+											"end";
+
+const char* LuaEnvironment::get_cmd_by_name = 	"function get_command_by_name(text) "
+												"	local cmds = get_all_commands() "
+												"	local results = {} "
+												"	local index = 0 "
+												"	for i,cmd in pairs(cmds) do "
+												"		if string.find(cmd, text) then "
+												"			results[index] = cmds[i] "
+												"			index = index + 1 "
+												"		end "
+												"	end "
+												"	return results "
+												"end";
+
+const char* LuaEnvironment::tmp_print_table =	"function print_table(table) "												
+												"	for k,v in pairs(table) do "
+												"		print(v) "
+												"	end "
+												"end";
+
+const char* LuaEnvironment::count_all = 	"function count_all(f) "
+											"	local seen = {} "
+											"	local count_table "
+											"	count_table = function(t) "
+											"		if seen[t] then return end "
+											"		f(t) "
+											"		seen[t] = true "
+											"		for k,v in pairs(t) do "
+											"			if type(v) == 'table' then "
+											"				count_table(v) "
+											"			elseif type(v) == 'userdata' then "
+											"				f(v) "
+											"			end "
+											"		end "
+											"	end "
+											"	count_table(_G) "
+											"end";
+
 } // namespace crown

+ 75 - 11
engine/lua/LuaEnvironment.h

@@ -29,27 +29,95 @@ OTHER DEALINGS IN THE SOFTWARE.
 #include "lua.hpp"
 #include "Types.h"
 #include "Config.h"
+#include "LuaStack.h"
+#include "LinearAllocator.h"
+#include "Thread.h"
 
 namespace crown
 {
 
+/// LuaEnvironment is a wrapper of a subset of Lua functions and 
+/// provides utilities for extending Lua
 class LuaEnvironment
 {
 
 public:
 	/// Constructor
-					LuaEnvironment(lua_State* L);
-	/// Load a function to proper module
-	void			load_module_function(const char* module, const char* name, const lua_CFunction func);
-	void			load_module_enum(const char* module, const char* name, uint32_t value);
-	/// Create library based on each module which will be opened by luaopen_*
-	void			create_module_library();
+							LuaEnvironment();
+	/// Init Lua state and open libraries. Must be called first
+	void					init();
+	/// Close Lua state and shutdown LuaEnvironment
+	void					shutdown();
+	/// Return the __lua_State__ pointer required by each Lua function
+	lua_State*				state();
+
+	const char*				error();
+	/// Load Lua chunk as buffer. @a buffer contains lua chunk, @len is length of buffer
+	void					load_buffer(const char* buffer, size_t len);
+	/// Load Lua chunk as file. @a file is path to lua file
+	void					load_file(const char* file);
+	/// Load Lua chunk as string
+	void 					load_string(const char* str);
+	/// Get global symbol from Lua stack. @symbol is the name of required symbol
+	void					get_global_symbol(const char* symbol);
+	/// Execute Lua chunk. @a args is the number of arguments required
+	/// @a results is the number of results returned
+	void					execute(int32_t args, int32_t results);
+	/// Collect garbage generated by Lua
+	void					collect_garbage();
+	/// Call init function in the Lua game file
+	void					game_init();
+	/// Call shutdown function in the Lua game file
+	void					game_shutdown();
+	/// Call frame function in the Lua game file
+	void					game_frame(float dt);
+
+	/// Load a function which will be used in Lua. @a module is the name of table contenitor,
+	/// @a name is the name of function in module and @func is the pointer to the function.
+	/// _func_ must be a C/lua function (__int32_t function_name(lua_State* L)__)
+	void					load_module_function(const char* module, const char* name, const lua_CFunction func);
+	/// Load a enum's value which will be used in Lua. 
+	/// @a module is the name of table contenitor, generally take  enum's name
+	/// @a name is module's name that refears _value_ and @value is an unsigned integer
+	void					load_module_enum(const char* module, const char* name, uint32_t value);
 
 private:
+	/// Thread used for garbage collection. It calls __collect_garbage()__
+	static void*			background_thread(void* thiz);
 
-	lua_State*		m_state;
+	void					lua_error();
+	// Disable copying
+							LuaEnvironment(const LuaEnvironment&);
+	LuaEnvironment& 		operator=(const LuaEnvironment&);
+
+private:
+	/// Required by each Lua function
+	lua_State*				m_state;
+	/// LuaEnvironment is used right now?
+	bool					m_is_used;
+	/// Thread used for garbage collection
+	os::Thread 				m_thread;
+
+	char					m_error_buffer[1024];
+
+	char					m_tmp_buffer[1024];
+
+	static const char* 		class_system;
+
+	static const char*		commands_list;
+
+	static const char*		get_cmd_by_name;
+
+	static const char*		tmp_print_table;
+
+	static const char*		count_all;
+
+	// static const char*		type_name;
+
+	// static const char*		type_count;
 };
 
+
 void load_int_setting(LuaEnvironment& env);
 void load_float_setting(LuaEnvironment& env);
 void load_string_setting(LuaEnvironment& env);
@@ -59,16 +127,12 @@ void load_vec3(LuaEnvironment& env);
 void load_mat4(LuaEnvironment& env);
 void load_quat(LuaEnvironment& env);
 void load_math(LuaEnvironment& env);
-
 void load_mouse(LuaEnvironment& env);
 void load_keyboard(LuaEnvironment& env);
 void load_touch(LuaEnvironment& env);
 void load_accelerometer(LuaEnvironment& env);
-
 void load_camera(LuaEnvironment& env);
-
 void load_device(LuaEnvironment& env);
-
 void load_window(LuaEnvironment& env);
 
 CE_EXPORT int32_t luaopen_libcrown(lua_State* L);

+ 22 - 0
engine/lua/LuaStack.cpp

@@ -88,6 +88,12 @@ LuaStack::LuaStack(lua_State* L)
 	m_state = L;
 }
 
+//-----------------------------------------------------------------------------
+lua_State* LuaStack::state()
+{
+	return m_state;
+}
+
 //-----------------------------------------------------------------------------
 void LuaStack::push_bool(bool value)
 {
@@ -130,6 +136,12 @@ void LuaStack::push_string(const char* str, size_t len)
 	lua_pushlstring(m_state, str, len);
 }
 
+//-----------------------------------------------------------------------------
+void LuaStack::push_lightdata(void* data, size_t len)
+{
+	lua_pushlightuserdata(m_state, data);
+}
+
 //-----------------------------------------------------------------------------
 void LuaStack::push_vec2(Vec2* v)
 {
@@ -186,6 +198,16 @@ const char* LuaStack::get_string(int32_t index)
 	return luaL_checkstring(m_state, index);
 }
 
+//-----------------------------------------------------------------------------
+void* LuaStack::get_lightdata(int32_t index)
+{
+	CE_ASSERT(lua_islightuserdata(m_state, index), "Not a lightuserdata object");
+
+	void* data = lua_touserdata(m_state, index);
+
+	return data;	
+}
+
 //-----------------------------------------------------------------------------
 Vec2* LuaStack::get_vec2(int32_t index)
 {

+ 6 - 0
engine/lua/LuaStack.h

@@ -49,6 +49,8 @@ public:
 
 							LuaStack(lua_State* L);
 
+	lua_State*				state();
+
 	void					push_bool(bool value);
 
 	void					push_int32(int32_t value);
@@ -63,6 +65,8 @@ public:
 
 	void 					push_string(const char* str, size_t len);
 
+	void					push_lightdata(void* data, size_t len);
+
 	void					push_vec2(Vec2* v);
 
 	void					push_vec3(Vec3* v);
@@ -79,6 +83,8 @@ public:
 
 	const char*				get_string(int32_t index);
 
+	void*					get_lightdata(int32_t index);
+
 	Vec2*					get_vec2(int32_t index);
 
 	Vec3*					get_vec3(int32_t index);

+ 3 - 0
engine/os/android/AndroidManifest.xml

@@ -6,6 +6,9 @@
     <uses-sdk
         android:minSdkVersion="9"/>
 
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.SET_DEBUG_APP"/>
+    
     <application android:label="Crown" >
         <activity android:name="CrownActivity"
                   android:label="Crown"

+ 1 - 1
engine/os/posix/TCPSocket.cpp

@@ -184,7 +184,7 @@ size_t TCPSocket::receive(void* data, size_t size)
 		return false;
 	}
 
-	ssize_t received_bytes = ::recv(m_active_socket, (char*) data, size, 0);
+	ssize_t received_bytes = ::read(m_active_socket, (char*) data, size);
 	if (received_bytes <= 0)
 	{
 		return 0;

+ 1 - 0
tools/CMakeLists.txt

@@ -78,4 +78,5 @@ install (FILES cli/resource-compiler.py PERMISSIONS OWNER_READ OWNER_WRITE OWNER
 
 add_subdirectory(gui/resource-browser)
 add_subdirectory(gui/toolchain)
+add_subdirectory(gui/console)
 add_subdirectory(pycrown)

+ 6 - 0
tools/gui/console/CMakeLists.txt

@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 2.8)
+
+install (FILES console.py PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
+	GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE DESTINATION tools)
+
+install (FILES ui/console.glade DESTINATION tools/ui)

+ 243 - 0
tools/gui/console/console.py

@@ -0,0 +1,243 @@
+#!/usr/bin/python
+
+# Copyright (c) 2013 Daniele Bartolini, Michele Rossi
+# Copyright (c) 2012 Daniele Bartolini, Simone Boscaratto
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+# 
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+# 
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+
+import sys
+import os
+import socket
+import threading
+
+from gi.repository import Gtk
+
+# Client Console commands
+CMD_CLEAR   = "clear"           # Clear console output
+CMD_EXIT_1  = "exit"            # Close console
+CMD_EXIT_2  = "Device.stop()"   # Close console
+CMD_HELP    = "help"            # Console help
+CMD_HISTORY = "history"         # History
+CMD_VOID    = ""
+
+
+class ReaderThread(threading.Thread):
+#------------------------------------------------------------------------------
+    def __init__(self, console):
+        threading.Thread.__init__(self)
+        self.t_console = console
+        self.t_stop = threading.Event()
+        self.t_is_running = True
+
+#------------------------------------------------------------------------------
+    def stop(self):
+        self.t_stop.set()
+        self.t_is_running = False
+
+
+#------------------------------------------------------------------------------
+    def stopped(self):
+        return self.t_stop.isSet()
+
+#------------------------------------------------------------------------------
+    def run(self):
+        while self.t_is_running:
+            self.t_tmp = self.t_console.m_sock.recv(1024)
+
+            if self.t_tmp != "":
+                self.t_tmp = self.t_tmp.decode("utf-8")
+                self.t_error = self.t_tmp.split('\x00', 1)[0]
+                self.t_console.print_error(self.t_error)
+
+
+class ConsoleHistory:
+#------------------------------------------------------------------------------
+    def __init__(self):
+        self.m_list = list()
+        self.m_count = 0
+        self.m_index = 0
+
+#------------------------------------------------------------------------------
+    def add(self, cmd):
+        self.m_list.append(cmd)
+        self.m_count += 1
+        self.m_index = self.m_count
+
+#------------------------------------------------------------------------------
+    def previous(self):
+        if self.m_count != 0:
+            self.m_index -= 1
+
+            if self.m_index < 0:
+                self.m_index = 0
+
+            return self.m_list[self.m_index]
+
+        return ""
+
+#------------------------------------------------------------------------------
+    def following(self):
+        if self.m_count != 0:
+            self.m_index += 1
+
+            if self.m_index > self.m_count-1:
+                self.m_index = self.m_count - 1
+
+            return self.m_list[self.m_index]
+
+        return ""
+
+
+class Console:
+#------------------------------------------------------------------------------
+    def __init__(self, address):   
+        builder = Gtk.Builder()
+        builder.add_from_file("ui/console.glade")
+        
+        self.m_view = builder.get_object("textview1")
+        self.m_buffer = builder.get_object("textbuffer1")
+        self.m_entry = builder.get_object("entry1")
+        
+        self.m_window = builder.get_object('window1')
+        self.m_window.set_title("Crown Console")      
+        self.m_window.show_all()
+
+        builder.connect_signals(self)
+
+        self.m_address = address
+        self.m_error_buffer = ""
+
+        self.history = ConsoleHistory()
+
+        self.m_sock = socket.create_connection((self.m_address, 10000))
+
+        self.m_thread = ReaderThread(self)
+        self.m_thread.start()
+
+        self.m_formatter = self.m_buffer.create_tag("console", foreground="green")
+
+        Gtk.main()
+
+#------------------------------------------------------------------------------
+    def on_destroy(self, *args):
+        self.m_sock.close()
+        self.m_thread.stop()
+        Gtk.main_quit(*args)
+
+#------------------------------------------------------------------------------
+    def on_key_pressed(self, entry, event):
+        # If return is pressed, run command
+        if event.keyval == 0xff0d:
+            cmd = entry.get_text()
+            self.parse_command(cmd)
+            return True
+
+        if event.keyval == 0xff52:
+            cmd = self.history.previous()
+            self.print_to_entry(cmd)
+            return True
+
+        if event.keyval == 0xff54:
+            cmd = self.history.following()
+            self.print_to_entry(cmd)
+            return True
+
+
+#------------------------------------------------------------------------------
+    def parse_command(self, cmd):
+        self.history.add(cmd)
+
+        if cmd == CMD_CLEAR:
+            self.m_buffer.set_text("")
+            self.m_entry.set_text("")
+
+        elif cmd == CMD_EXIT_1:
+            self.on_destroy()  
+
+        elif cmd == CMD_EXIT_2:
+            self.run_command(cmd)                  
+            self.on_destroy() 
+
+        elif cmd == CMD_HELP:
+            self.print_help()
+
+        elif cmd == CMD_VOID:
+            self.print_command("");
+
+        else:    
+            self.run_command(cmd)        
+
+#------------------------------------------------------------------------------
+    def run_command(self, cmd):
+        self.m_sock.send(cmd.encode('utf-8'))
+        self.print_command(cmd)
+
+#------------------------------------------------------------------------------
+    def print_command(self, text):
+        # Print command to console
+        start_iter = self.m_buffer.get_end_iter()       
+
+        a_string = "> " + text + "\n"
+        # Append command to the end of buffer
+
+        self.m_buffer.insert(start_iter, a_string, len(a_string))
+
+        self.m_view.scroll_mark_onscreen(self.m_buffer.get_insert())
+
+        # Reset entry
+        self.print_to_entry("")
+
+#------------------------------------------------------------------------------
+    def print_error(self, text):
+        start_iter = self.m_buffer.get_end_iter()  
+
+        a_string = "> " + text + "\n"
+
+        self.m_buffer.insert(start_iter, a_string, len(a_string))
+
+        self.m_view.scroll_mark_onscreen(self.m_buffer.get_insert())
+
+#------------------------------------------------------------------------------
+    def print_to_entry(self, text):
+        self.m_entry.set_text(text)
+
+#------------------------------------------------------------------------------
+    # def popup_dialog(self, message, expl):
+    #     dialog = Gtk.MessageDialog(self.m_window, 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, message)
+    #     dialog.format_secondary_text(expl)
+    #     dialog.run()
+    #     dialog.destroy()
+
+#------------------------------------------------------------------------------
+#------------------------------------------------------------------------------
+def main():
+    if len(sys.argv) != 2:
+        print("Usage: console.py <ip-address>")
+        exit(-1)
+
+    address = sys.argv[1]
+
+    console = Console(address)
+
+
+main()
+    

+ 59 - 0
tools/gui/console/ui/console.glade

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.6 -->
+  <object class="GtkTextBuffer" id="textbuffer1"/>
+  <object class="GtkWindow" id="window1">
+    <property name="can_focus">False</property>
+    <property name="border_width">6</property>
+    <property name="default_width">600</property>
+    <property name="default_height">330</property>
+    <signal name="destroy" handler="on_destroy" swapped="no"/>
+    <child>
+      <object class="GtkBox" id="box1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkEntry" id="entry1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="has_focus">True</property>
+            <property name="progress_pulse_step">0</property>
+            <signal name="key-press-event" handler="on_key_pressed" swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="hscrollbar_policy">never</property>
+            <property name="vscrollbar_policy">always</property>
+            <property name="shadow_type">in</property>
+            <child>
+              <object class="GtkTextView" id="textview1">
+                <property name="height_request">339</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="editable">False</property>
+                <property name="cursor_visible">False</property>
+                <property name="buffer">textbuffer1</property>
+                <property name="accepts_tab">False</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>

+ 6 - 3
tools/gui/toolchain/toolchain.py

@@ -76,11 +76,14 @@ class Toolchain:
 		root_path = str(self.m_root_path)
 		dest_path = root_path + "_" + self.m_current_platform
 
-		comp = subprocess.call(["python", "resource-compiler.py", str(self.m_root_path), self.m_current_platform])
-		crown = subprocess.call(["../bin/crown-linux", "--root-path", dest_path, "--dev"])
+		comp = subprocess.Popen(["python", "resource-compiler.py", str(self.m_root_path), self.m_current_platform])
+		crown = subprocess.Popen(["../bin/crown-linux", "--root-path", dest_path, "--dev"])
 
 	def on_browser_button_clicked(self, button):
-		browser = subprocess.call(["python", "resource-browser.py", str(self.m_root_path)])
+		browser = subprocess.Popen(["python", "resource-browser.py", str(self.m_root_path)])
+
+	def on_console_button_clicked(self, button):
+		console = subprocess.Popen(["python", "console.py", "localhost"])
 
 
 #------------------------------------------------------------------------------

+ 2 - 1
tools/gui/toolchain/ui/toolchain.glade

@@ -226,10 +226,11 @@
             </child>
             <child>
               <object class="GtkButton" id="button4">
-                <property name="label" translatable="yes">Something else</property>
+                <property name="label" translatable="yes">Console</property>
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">True</property>
+                <signal name="clicked" handler="on_console_button_clicked" swapped="no"/>
               </object>
               <packing>
                 <property name="expand">False</property>