diff --git a/client/src/client_network.cpp b/client/src/client_network.cpp index b36a683..74e52c8 100644 --- a/client/src/client_network.cpp +++ b/client/src/client_network.cpp @@ -66,8 +66,10 @@ std::string ClientNetwork::receive_response(void) { memset(buffer, 0, sizeof(buffer)); void* socket_ptr = _socket; + /* Wait up to 2 seconds for data to be available. */ if(NET_WaitUntilInputAvailable(&socket_ptr, 1, 2000) > 0) { - if(NET_ReadFromStreamSocket(_socket, buffer, sizeof(buffer)) > 0) { + int bytes_received = NET_ReadFromStreamSocket(_socket, buffer, sizeof(buffer)-1); + if(bytes_received > 0) { return std::string(buffer); } } diff --git a/client/src/terminal.cpp b/client/src/terminal.cpp index c3e44ff..62ffc89 100644 --- a/client/src/terminal.cpp +++ b/client/src/terminal.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,13 +11,14 @@ Terminal::Terminal(void) { /* Placeholder welcome message to history. */ _history.push_back("Welcome to Bettola"); - _scroll_offset = 0; _network = new ClientNetwork(); if(_network->connect("localhost", 8080)) { _history.push_back("Connection to server Success!"); } else { _history.push_back("Connection to server failed!"); } + _current_path = "/"; + _scroll_offset = 0; } Terminal::~Terminal(void) { @@ -33,8 +35,16 @@ void Terminal::_on_ret_press(void) { printf("Command entered: %s\n", _input_buffer.c_str()); _network->send_command(_input_buffer.c_str()); std::string response = _network->receive_response(); - if(!response.empty()) { - _history.push_back(response); + /* Check if the response is a new path for the prompt. */ + if(response.rfind("/", 0) == 0 && response.find('\n') == std::string::npos) { + _current_path = response; + } else if(!response.empty()) { + /* Otherwise, it's command out. Split it by newline and add to history. */ + std::stringstream ss(response); + std::string line; + while(std::getline(ss, line, '\n')) { + _history.push_back(line); + } } if(_input_buffer == "clear") { @@ -106,7 +116,7 @@ void Terminal::render(TextRenderer* renderer, int x, int y, int width, int heigh } /* Draw current input line. */ - std::string line_to_render = "> " + _input_buffer; + std::string line_to_render = _current_path + "> " + _input_buffer; if(show_cursor) { line_to_render += "_"; } diff --git a/client/src/terminal.h b/client/src/terminal.h index ab7a938..538a9c3 100644 --- a/client/src/terminal.h +++ b/client/src/terminal.h @@ -21,5 +21,6 @@ private: std::string _input_buffer; std::vector _history; int _scroll_offset; + std::string _current_path; ClientNetwork* _network; }; diff --git a/common/src/vfs.cpp b/common/src/vfs.cpp new file mode 100644 index 0000000..19a1282 --- /dev/null +++ b/common/src/vfs.cpp @@ -0,0 +1,11 @@ +#include "vfs.h" + +std::string get_full_path(vfs_node* node) { + if(node->parent == nullptr) { + return "/"; + } + if(node->parent->parent == nullptr) { /* If parent is root. */ + return "/" + node->name; + } + return get_full_path(node->parent) + "/" + node->name; +} diff --git a/common/src/vfs.h b/common/src/vfs.h index 3d99743..1f02dc1 100644 --- a/common/src/vfs.h +++ b/common/src/vfs.h @@ -24,3 +24,5 @@ struct vfs_node { vfs_child_map children; vfs_node* parent; }; + +std::string get_full_path(vfs_node* node); diff --git a/server/src/network_manager.cpp b/server/src/network_manager.cpp index ee47137..5b5cbfb 100644 --- a/server/src/network_manager.cpp +++ b/server/src/network_manager.cpp @@ -1,6 +1,7 @@ #include #include +#include "SDL_net.h" #include "vfs.h" #include "network_manager.h" @@ -72,8 +73,37 @@ void NetworkManager::_handle_client_activity(void) { } void NetworkManager::_process_command(Player* player, char* command) { - /* We'll just do 'ls' for now. */ - if(strncmp(command, "ls", 2) == 0) { + /* Remove trailing newline for command comparison. */ + std::string cmd_str = command; + if(!cmd_str.empty() && cmd_str.back() == '\n') { + cmd_str.pop_back(); + } + if(strncmp(command, "cd ", 3) == 0) { + /* Handle 'cd' command. */ + std::string target_dir_name = command + 3; + /* Remove trailing newline if it exists. */ + if(!target_dir_name.empty() && target_dir_name.back() == '\n') { + target_dir_name.pop_back(); + } + + if(target_dir_name == "..") { + if(player->current_dir->parent) { + player->current_dir = player->current_dir->parent; + } + } else if(player->current_dir->children.count(target_dir_name)) { + vfs_node* target_node = player->current_dir->children[target_dir_name]; + if(target_node->type == DIR_NODE) { + player->current_dir = target_node; + } else { + /* It's a file, not a directory. */ + NET_WriteToStreamSocket(player->socket, "cd: not a directory\n", 22); + return; + } + } + /* On successful cd, send back the new path for the prompt. */ + std::string new_path = get_full_path(player->current_dir); + NET_WriteToStreamSocket(player->socket, new_path.c_str(), new_path.length()+1); + } else if(cmd_str == "ls") { std::string response = ""; if(player->current_dir && player->current_dir->type == DIR_NODE) { for(auto const& [name, node] : player->current_dir->children) { @@ -81,11 +111,18 @@ void NetworkManager::_process_command(Player* player, char* command) { if(node->type == DIR_NODE) { response += "/"; } - response += "\n"; + response += " "; } } /* NET_WriteToStreamSocket is essentially the new send function in SDL3. */ NET_WriteToStreamSocket(player->socket, response.c_str(), response.length()+1); + } else if(cmd_str == "clear") { + /* Send an empty reply to unblock. */ + NET_WriteToStreamSocket(player->socket, "", 1); + } else { + /* Unknown command. */ + std::string response = "Unknown command: " + cmd_str + "\n"; + NET_WriteToStreamSocket(player->socket, response.c_str(), response.length()+1); } }