Przeglądaj źródła

Add basic file server capabilities to ConsoleServer

Daniele Bartolini 12 lat temu
rodzic
commit
9b99039330
2 zmienionych plików z 116 dodań i 82 usunięć
  1. 109 72
      engine/ConsoleServer.cpp
  2. 7 10
      engine/ConsoleServer.h

+ 109 - 72
engine/ConsoleServer.cpp

@@ -32,24 +32,26 @@ OTHER DEALINGS IN THE SOFTWARE.
 #include "Device.h"
 #include "ProxyAllocator.h"
 #include "LuaEnvironment.h"
+#include "File.h"
+#include "Filesystem.h"
+#include "MathUtils.h"
 
 namespace crown
 {
 
 //-----------------------------------------------------------------------------
-ConsoleServer::ConsoleServer()
-	: m_num_clients(0)
+ConsoleServer::ConsoleServer(uint16_t port)
+	: m_port(port)
+	, m_num_clients(0)
 	, m_receive_buffer(default_allocator())
-	, m_send_buffer(default_allocator())
 	, m_receive_callbacks(default_allocator())
-	, m_send_callbacks(default_allocator())
 {
 }
 
 //-----------------------------------------------------------------------------
 void ConsoleServer::init(bool wait)
 {
-	m_listener.open(10001);
+	m_listener.open(m_port);
 
 	if (wait)
 	{
@@ -98,14 +100,9 @@ void ConsoleServer::log_to_all(const char* message, LogSeverity::Enum severity)
 //-----------------------------------------------------------------------------
 void ConsoleServer::send_message_to(ClientId client, const char* message)
 {
-	RPCCallback cb;
-	cb.client = client;
-	cb.message_index = m_send_buffer.size();
-
-	m_send_buffer.push(message, string::strlen(message));
-	m_send_buffer.push_back('\0');
-
-	m_send_callbacks.push_back(cb);
+	uint32_t msg_len = string::strlen(message);
+	m_clients[client.index].write((const char*) &msg_len, 4);
+	m_clients[client.index].write(message, msg_len);
 }
 
 //-----------------------------------------------------------------------------
@@ -141,6 +138,9 @@ void ConsoleServer::update()
 			update_client(id);
 		}
 	}
+
+	// Process all requests
+	process_requests();
 }
 
 //-----------------------------------------------------------------------------
@@ -154,72 +154,43 @@ void ConsoleServer::process_requests()
 		const char* request = &m_receive_buffer[cb.message_index];
 		JSONParser parser(request);
 		JSONElement request_type = parser.root().key("type");
-		const char* type = request_type.string_value();
+		DynamicString type; 
+		request_type.string_value(type);
 
 		// Determine request type
-		if (string::strcmp("ping", type) == 0) process_ping(cb.client, request);
-		else if (string::strcmp("script", type) == 0) process_script(cb.client, request);
-		else if (string::strcmp("stats", type) == 0) process_stats(cb.client, request);
-		else if (string::strcmp("command", type) == 0) process_command(cb.client, request);
+		if (type == "ping") process_ping(cb.client, request);
+		else if (type == "script") process_script(cb.client, request);
+		else if (type == "stats") process_stats(cb.client, request);
+		else if (type == "command") process_command(cb.client, request);
+		else if (type == "filesystem") processs_filesystem(cb.client, request);
 		else continue;
 	}
 
 	m_receive_callbacks.clear();
 	m_receive_buffer.clear();
-
-	for (uint32_t i = 0; i < m_send_callbacks.size(); i++)
-	{
-		RPCCallback cb = m_send_callbacks.front();
-	 	m_send_callbacks.pop_front();
-
-	 	m_clients[cb.client.index].write(&m_send_buffer[cb.message_index], string::strlen(&m_send_buffer[cb.message_index]));
-	}
-
-	m_send_callbacks.clear();
-	m_send_buffer.clear();
 }
 
 //-----------------------------------------------------------------------------
 void ConsoleServer::update_client(ClientId id)
 {
-	size_t total_read = 0;
-	uint32_t message_index = m_receive_buffer.size();
-
 	TCPSocket& client = m_clients[id.index];
 
-	char buf[1024];
-	while (true)
-	{
-		ReadResult result = client.read(buf, 32);
+	uint32_t msg_len = 0;
+	ReadResult rr = client.read_nonblock(&msg_len, 4);
 
-		if (result.error == ReadResult::NO_RESULT_ERROR)
-		{
-			if (result.received_bytes > 0)
-			{
-				m_receive_buffer.push(buf, result.received_bytes);
-				total_read += result.received_bytes;
-				continue;
-			}
-
-			break;
-		}
-		else
-		{
-			// Close remote connection
-			client.close();
-			remove_client(id);
-			return;
-		}
-	}
+	// If no data received, return
+	if (rr.error == ReadResult::NO_ERROR && rr.received_bytes == 0) return;
+	if (rr.error == ReadResult::REMOTE_CLOSED) return;
 
-	// Ensure NUL-terminated
+	// Else read the message
+	List<char> msg_buf(default_allocator());
+	msg_buf.resize(msg_len);
+	ReadResult msg_result = client.read(msg_buf.begin(), msg_len);
+	
+	uint32_t message_index = m_receive_buffer.size();
+	m_receive_buffer.push(msg_buf.begin(), msg_result.received_bytes);
 	m_receive_buffer.push_back('\0');
-
-	// Process only if received something
-	if (total_read > 0)
-	{
-		add_request(id, message_index);
-	}
+	add_request(id, message_index);
 }
 
 //-----------------------------------------------------------------------------
@@ -268,8 +239,9 @@ void ConsoleServer::process_script(ClientId /*client*/, const char* msg)
 	JSONParser parser(msg);
 	JSONElement root = parser.root();
 
-	const char* script = root.key("script").string_value();
-	device()->lua_environment()->execute_string(script);
+	DynamicString script;
+	root.key("script").string_value(script);
+	device()->lua_environment()->execute_string(script.c_str());
 }
 
 //-----------------------------------------------------------------------------
@@ -305,27 +277,92 @@ void ConsoleServer::process_command(ClientId /*client*/, const char* msg)
 	JSONElement root = parser.root();
 	JSONElement command = root.key("command");
 
-	const char* cmd = command.string_value();
+	DynamicString cmd;
+	command.string_value(cmd);
 
-	if (string::strcmp("reload", cmd) == 0)
+	if (cmd == "reload")
 	{
-		JSONElement resource_type = root.key_or_nil("resource_type");
-		JSONElement resource_name = root.key_or_nil("resource_name");
+		JSONElement type = root.key_or_nil("resource_type");
+		JSONElement name = root.key_or_nil("resource_name");
+
+		DynamicString resource_type;
+		DynamicString resource_name;
+		type.string_value(resource_type);
+		name.string_value(resource_name);
 
 		char t[256];
 		char n[256];
-		string::strncpy(t, resource_type.string_value(), 256);
-		string::strncpy(n, resource_name.string_value(), 256);
+		string::strncpy(t, resource_type.c_str(), 256);
+		string::strncpy(n, resource_name.c_str(), 256);
 		device()->reload(t, n);
 	}
-	else if (string::strcmp("pause", cmd) == 0)
+	else if (cmd == "pause")
 	{
 		device()->pause();
 	}
-	else if (string::strcmp("unpause", cmd) == 0)
+	else if (cmd == "unpause")
 	{
 		device()->unpause();
 	}
 }
 
+//-----------------------------------------------------------------------------
+void ConsoleServer::processs_filesystem(ClientId client, const char* msg)
+{
+	JSONParser parser(msg);
+	JSONElement root = parser.root();
+	JSONElement filesystem = root.key("filesystem");
+
+	DynamicString cmd;
+	filesystem.string_value(cmd);
+
+	if (cmd == "size")
+	{
+		DynamicString file_name;
+		root.key("file").string_value(file_name);
+
+		File* file = device()->filesystem()->open(file_name.c_str(), FOM_READ);
+		size_t file_size = file->size();
+		device()->filesystem()->close(file);
+
+		TempAllocator64 alloc;
+		StringStream response(alloc);
+		response << "{\"type\":\"file\",\"size\":" << file_size << "}";
+
+		send_message_to(client, response.c_str());
+	}
+	else if (cmd == "read")
+	{
+		JSONElement file_position = root.key("position");
+		JSONElement file_size = root.key("size");
+
+		DynamicString file_name;
+		root.key("file").string_value(file_name);
+
+		// Read the file data
+		File* file = device()->filesystem()->open(file_name.c_str(), FOM_READ);
+		char* bytes = (char*) default_allocator().allocate((size_t) file_size.int_value());
+		file->seek((size_t) file_position.int_value());
+		file->read(bytes, (size_t) file_size.int_value());
+		device()->filesystem()->close(file);
+
+		// Encode data to base64
+		size_t dummy;
+		char* bytes_encoded = math::base64_encode((unsigned char*) bytes, (size_t) file_size.int_value(), &dummy);
+
+		// Send data
+		TempAllocator4096 alloc;
+		StringStream response(alloc);
+
+		response << "{\"type\":\"file\",";
+		response << "\"data\":\"" << bytes_encoded << "\"}";
+
+		send_message_to(client, response.c_str());
+
+		// Cleanup
+		default_allocator().deallocate(bytes_encoded);
+		default_allocator().deallocate(bytes);
+	}
+}
+
 } // namespace crown

+ 7 - 10
engine/ConsoleServer.h

@@ -36,7 +36,7 @@ namespace crown
 {
 
 typedef Id ClientId;
-#define MAX_CONSOLE_CLIENTS 16
+#define MAX_CONSOLE_CLIENTS 100
 
 struct RPCCallback
 {
@@ -48,7 +48,8 @@ class ConsoleServer
 {
 public:
 
-								ConsoleServer();
+	/// Listens on the given @a port.
+								ConsoleServer(uint16_t port);
 
 	/// Initializes the system. If @a wait is true, this function
 	/// blocks until a client is connected.
@@ -60,15 +61,12 @@ public:
 	void						send_message_to(ClientId client, const char* message);
 	void						send_message_to_all(const char* message);
 
-	/// Collects requests from clients without processing them.
+	/// Collects requests from clients and processes them all.
 	void						update();
 
-	/// Processes all the requests collected by update() possibly accessing
-	/// global resources. It should be called only when "it is safe".
-	void						process_requests();
-
 private:
 
+	void						process_requests();
 	void						update_client(ClientId id);
 	void						add_client(TCPSocket& client);
 	void						remove_client(ClientId id);
@@ -78,9 +76,11 @@ private:
 	void						process_script(ClientId client, const char* msg);
 	void						process_stats(ClientId client, const char* msg);
 	void						process_command(ClientId client, const char* msg);
+	void						processs_filesystem(ClientId client, const char* msg);
 
 private:
 
+	uint16_t					m_port;
 	TCPListener					m_listener;
 
 	uint8_t						m_num_clients;
@@ -88,10 +88,7 @@ private:
 	TCPSocket					m_clients[MAX_CONSOLE_CLIENTS];
 
 	List<char>					m_receive_buffer;
-	List<char>					m_send_buffer;
-
 	Queue<RPCCallback>			m_receive_callbacks;
-	Queue<RPCCallback>			m_send_callbacks;
 };
 
 } // namespace crown