diff --git a/assets/scripts/bin/cd.lua b/assets/scripts/bin/cd.lua new file mode 100644 index 0000000..fb47f56 --- /dev/null +++ b/assets/scripts/bin/cd.lua @@ -0,0 +1,7 @@ +-- /bin/cd - Change Directory. +local target = arg[1] +if not target then + return "" -- No argument, just return to prompt. +end + +return { action = "cd", target = target } diff --git a/assets/scripts/bin/exit.lua b/assets/scripts/bin/exit.lua new file mode 100644 index 0000000..888bfd1 --- /dev/null +++ b/assets/scripts/bin/exit.lua @@ -0,0 +1,6 @@ +-- /bin/exit - Disconnects from a remote session or cloes terminal window. +if is_remote_session then + return { action = "disconnect" } +else + return { action = "close_terminal" } +end diff --git a/client/src/client_network.cpp b/client/src/client_network.cpp index b9a912a..0b8e5ac 100644 --- a/client/src/client_network.cpp +++ b/client/src/client_network.cpp @@ -52,6 +52,17 @@ bool ClientNetwork::connect(const std::string& host, uint16_t port) { } void ClientNetwork::disconnect(void) { + /* Stop the context, should cause the io_context.run() call in the + * background thread to return. + */ + _io_context.stop(); + + /* Wait for the background thread to finish. */ + if(_context_thread.joinable()) { + _context_thread.join(); + } + + /* Should be safe to close the socket no that thread is stoped */ if(is_connected()) { /* Close the socket. Causes outstanding async operations * in TcpConnection to compete with an error, which in return diff --git a/client/src/terminal.cpp b/client/src/terminal.cpp index e69f23c..7f436e0 100644 --- a/client/src/terminal.cpp +++ b/client/src/terminal.cpp @@ -32,6 +32,11 @@ Terminal::~Terminal(void) {} void Terminal::update(void) { std::string server_msg; while(_network->poll_message(server_msg)) { + if(server_msg == "__CLOSE_CONNECTION__") { + _history.push_back("Connection closed by server."); + _should_close = true; + return; + } /* Server will send "output\nprompt". Split them. */ size_t last_newline = server_msg.find_last_of('\n'); if(last_newline != std::string::npos) { @@ -59,13 +64,7 @@ void Terminal::_on_ret_press(void) { /* Add the command to history. */ _history.push_back(_prompt + "> " + command); - if(command == "exit") { - _should_close = true; - /* TODO: Window will close, and the OS will close the socket. - * server will detect the disconnection. - */ - return; - } else if(command == "clear"){ + if(command == "clear") { _history.clear(); return; } diff --git a/common/src/command_processor.cpp b/common/src/command_processor.cpp index 6906f53..cc1b444 100644 --- a/common/src/command_processor.cpp +++ b/common/src/command_processor.cpp @@ -1,15 +1,17 @@ #include "command_processor.h" -#include #include + #include "vfs.h" #include "lua_processor.h" #include "vfs_manager.h" -CommandProcessor::CommandProcessor(vfs_node* starting_dir, +CommandProcessor::CommandProcessor(vfs_node* home_vfs, std::map& world_vfs) : _world_vfs(world_vfs) { - _current_dir = starting_dir; _lua = new LuaProcessor(); + home_vfs_root = home_vfs; + session_vfs_root = home_vfs_root; + _current_dir = session_vfs_root; } CommandProcessor::~CommandProcessor(void) { @@ -46,7 +48,8 @@ std::string CommandProcessor::process_command(const std::string& command) { } if(root->children.count("bin") && root->children["bin"]->children.count(script_filename)) { vfs_node* script_node = root->children["bin"]->children[script_filename]; - sol::object result = _lua->execute(script_node->content, _current_dir, args); + bool is_remote = (session_vfs_root != home_vfs_root); + sol::object result = _lua->execute(script_node->content, _current_dir, args, is_remote); if(result.is()) { return result.as(); } else if(result.is()) { @@ -54,29 +57,7 @@ std::string CommandProcessor::process_command(const std::string& command) { } return "[Script returned an unexpected type]"; } - - - /* === Tmp fallback for built-in C++ commands. */ - if(command.rfind("cd ", 0) == 0) { - std::string target_dir_name = command.substr(3); - if(target_dir_name == "..") { - if(_current_dir->parent) { - _current_dir = _current_dir->parent; - } - } else if(_current_dir->children.count(target_dir_name)) { - vfs_node* target_node = _current_dir->children[target_dir_name]; - if(target_node->type == DIR_NODE) { - _current_dir = target_node; - } else { - return "cd: not a directory\n"; - } - } else { - return "cd: no such file or directory\n"; - } - return get_full_path(_current_dir); - } - - return "Unknown command: " + command + "\n"; + return "Unknown command: " + command_name + "\n"; } std::string CommandProcessor::_handle_vfs_action(sol::table action) { @@ -130,10 +111,34 @@ std::string CommandProcessor::_handle_vfs_action(sol::table action) { } else if(action_name == "ssh") { std::string target_ip = action["target"].get_or(""); if(_world_vfs.count(target_ip)) { - _current_dir = _world_vfs[target_ip]; + session_vfs_root = _world_vfs[target_ip]; + _current_dir = session_vfs_root; return "Connected to " + target_ip; } return "ssh: Could not resolve hostname " + target_ip + ": Name or service not known"; + } else if(action_name == "cd") { + std::string target_dir_name = action["target"].get_or(""); + if(target_dir_name == "..") { + if(_current_dir->parent) { + _current_dir = _current_dir->parent; + } + } else if(_current_dir->children.count(target_dir_name)) { + vfs_node* target_node = _current_dir->children[target_dir_name]; + if(target_node->type == DIR_NODE) { + _current_dir = target_node; + } else { + return std::string("cd: not a directory: ") + target_dir_name; + } + } else { + return std::string("cd: no such file or directory: ") + target_dir_name; + } + return ""; /* Success. */ + } else if(action_name == "disconnect") { + session_vfs_root = home_vfs_root; + _current_dir = home_vfs_root; + return "Connection closed."; + } else if(action_name == "close_terminal") { + return "__CLOSE_CONNECTION__"; } return "Error: Unknown VFS action '" + action_name + "'"; } diff --git a/common/src/command_processor.h b/common/src/command_processor.h index cd859e8..8ab2630 100644 --- a/common/src/command_processor.h +++ b/common/src/command_processor.h @@ -1,14 +1,14 @@ #pragma once +#include #include #include #include "vfs.h" -#include class CommandProcessor { public: - CommandProcessor(vfs_node* starting_dir, std::map& world_vfs); + CommandProcessor(vfs_node* home_vfs, std::map& world_vfs); ~CommandProcessor(void); std::string process_command(const std::string& command); @@ -16,6 +16,8 @@ public: private: std::string _handle_vfs_action(sol::table action); + vfs_node* home_vfs_root; + vfs_node* session_vfs_root; vfs_node* _current_dir; std::map& _world_vfs; LuaProcessor* _lua; diff --git a/common/src/lua_processor.cpp b/common/src/lua_processor.cpp index bb395da..0acdffb 100644 --- a/common/src/lua_processor.cpp +++ b/common/src/lua_processor.cpp @@ -17,9 +17,10 @@ LuaProcessor::LuaProcessor(void) { LuaProcessor::~LuaProcessor(void) {} sol::object LuaProcessor::execute(const std::string& script, vfs_node* current_dir, - const std::vector& args) { + const std::vector& args, bool is_remote) { try { /* Pass C++ objects/points into the Lua env. */ + _lua["is_remote_session"] = is_remote; _lua["current_dir"] = current_dir; /* Create and populate the 'arg' table for the script. */ diff --git a/common/src/lua_processor.h b/common/src/lua_processor.h index e3dddc2..b1b1bac 100644 --- a/common/src/lua_processor.h +++ b/common/src/lua_processor.h @@ -12,7 +12,7 @@ public: /* Executes a string of lua code and returns result as a string. */ sol::object execute(const std::string& script, vfs_node* current_dir, - const std::vector& args); + const std::vector& args, bool is_remote); private: sol::state _lua; }; diff --git a/common/src/net/tcp_connection.cpp b/common/src/net/tcp_connection.cpp index 5731fcd..874b9cd 100644 --- a/common/src/net/tcp_connection.cpp +++ b/common/src/net/tcp_connection.cpp @@ -78,7 +78,9 @@ void TcpConnection::async_read_header(void) { /* Wait for next message. */ async_read_header(); } else { - _socket.close(); + if(_socket.is_open()) { + _socket.close(); + } if(_on_disconnect) _on_disconnect(); } }); @@ -92,7 +94,9 @@ void TcpConnection::async_read_header(void) { /* Peer disconnected cleanly? */ if(ec != asio::error::eof) { } - _socket.close(); + if(_socket.is_open()) { + _socket.close(); + } if(_on_disconnect) _on_disconnect(); } }); @@ -110,7 +114,9 @@ void TcpConnection::async_write_header() { async_write_header(); } } else { - _socket.close(); + if(_socket.is_open()) { + _socket.close(); + } if(_on_disconnect) _on_disconnect(); } }); diff --git a/server/src/network_manager.cpp b/server/src/network_manager.cpp index 55ba40a..806e735 100644 --- a/server/src/network_manager.cpp +++ b/server/src/network_manager.cpp @@ -97,6 +97,13 @@ void NetworkManager::on_message(std::shared_ptr connection, fprintf(stderr, "[Player %u] Command: '%s'\n", player->id, message.c_str()); std::string response = player->cmd_processor->process_command(message); + + if(response == "__CLOSE_CONNECTION__") { + connection->send(response); + /* Just let me close the f.cking terminal? */ + return; + } + std::string new_prompt = get_full_path(player->cmd_processor->get_current_dir()); response += "\n" + new_prompt; diff --git a/server/src/player.cpp b/server/src/player.cpp index 8872e16..4bb95a7 100644 --- a/server/src/player.cpp +++ b/server/src/player.cpp @@ -3,14 +3,15 @@ Player::Player(uint32_t new_id, VFSManager& vfs_manager, std::map& world_vfs) : - id(new_id), - vfs_root(vfs_manager.create_vfs("player")), - cmd_processor(new CommandProcessor(vfs_root, world_vfs)) {} + id(new_id) { + vfs_node* player_vfs = vfs_manager.create_vfs("player"); + cmd_processor = new CommandProcessor(player_vfs, world_vfs); +} Player::~Player(void) { if(cmd_processor) { delete cmd_processor; } - /* TODO: The VFSManager should handle deleting the vfs_root. */ + /* TODO: The VFSManager should handle deleting the player_vfs_root. */ } diff --git a/server/src/player.h b/server/src/player.h index a8feece..fe71397 100644 --- a/server/src/player.h +++ b/server/src/player.h @@ -2,9 +2,11 @@ #include +#include "command_processor.h" #include "vfs.h" #include "vfs_manager.h" -#include "command_processor.h" + +class CommandProcessor; class Player { public: @@ -12,6 +14,5 @@ public: ~Player(void); uint32_t id; - vfs_node* vfs_root; CommandProcessor* cmd_processor; /* Manages the VFS state for the remote session. */ };