From 666b4f554bc6388e76d0081450e41a4ae864fb34 Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Wed, 1 Oct 2025 21:38:33 +0100 Subject: [PATCH] [Add] File loading and fix local 'exit' command. --- client/src/game_state.cpp | 43 +++++++++++++++++++++----------- client/src/terminal.cpp | 33 ++++++++++++++++-------- client/src/terminal.h | 2 ++ client/src/ui/desktop.cpp | 36 ++++++++++++++++++-------- client/src/ui/desktop.h | 2 -- client/src/ui/editor.cpp | 4 +++ client/src/ui/editor.h | 1 + client/src/ui/ui_window.cpp | 7 ++++++ client/src/ui/ui_window.h | 1 + client/src/ui/window_action.h | 4 ++- common/src/command_processor.cpp | 9 +++++++ common/src/command_processor.h | 1 + common/src/ui/text_buffer.cpp | 11 ++++++++ common/src/ui/text_buffer.h | 1 + server/src/network_manager.cpp | 8 ++++++ 15 files changed, 125 insertions(+), 38 deletions(-) diff --git a/client/src/game_state.cpp b/client/src/game_state.cpp index 6023a0a..da3b377 100644 --- a/client/src/game_state.cpp +++ b/client/src/game_state.cpp @@ -4,7 +4,7 @@ #include #include -#include "../../server/src/network_manager.h" +#include "network_manager.h" #include "game_state.h" #include "gfx/shape_renderer.h" #include "gfx/txt_renderer.h" @@ -12,7 +12,7 @@ #include "ui/desktop.h" #include "ui/i_window_content.h" #include "ui/ui_window.h" -#include "ui/window_action.h" +#include "ui/editor.h" #include #include @@ -128,6 +128,33 @@ void GameState::update(void) { case Screen::DESKTOP: { std::string server_msg; while(_network->poll_message(server_msg)) { + /* Check for 'special', non-terminal messages first. */ + if(server_msg.rfind("FILEDATA::", 0) == 0) { + std::string payload = server_msg.substr(10); + size_t separator_pos = payload.find("::"); + if(separator_pos != std::string::npos) { + std::string filepath = payload.substr(0, separator_pos); + std::string content = payload.substr(separator_pos+2); + auto editor = std::make_unique(); + editor->set_buffer_content(content); + auto editor_window = std::make_unique(filepath.c_str(), + 200, 200, 600, 400); + editor_window->set_content(std::move(editor)); + _desktop->add_window(std::move(editor_window)); + } + continue; + } + if(server_msg == "__DISCONNECTED__") { + IWindowContent* content = _desktop->get_focused_window() ? + _desktop->get_focused_window()->get_content() : nullptr; + Terminal* terminal = dynamic_cast(content); + if(terminal) { + terminal->add_history("Connection closed."); + _network->send(""); + } + continue; + } + /* If not 'special' message, assume it's for terminal. */ IWindowContent* content = _desktop->get_focused_window() ? _desktop->get_focused_window()->get_content() : nullptr; Terminal* terminal = dynamic_cast(content); @@ -154,18 +181,6 @@ void GameState::update(void) { } } if(_desktop) { - WindowAction action = _desktop->get_pending_action(); - switch(action.type) { - case ActionType::WRITE_FILE: { - std::string message = "WRITEF::" + action.payload1 + "::" + action.payload2; - _network->send(message); - break; - } - case ActionType::NONE: - default: - /* Do nothing. */ - break; - } /* * TODO: These fuck'in window dimensions just need to be global at this point!! * Pass GameState by reference ? diff --git a/client/src/terminal.cpp b/client/src/terminal.cpp index fcca88d..f8afa4e 100644 --- a/client/src/terminal.cpp +++ b/client/src/terminal.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -12,7 +13,7 @@ Terminal::Terminal(ClientNetwork* network) : _network(network), _should_close(false), _scroll_offset(0), - _prompt("") { + _prompt(""), _pending_action({ActionType::NONE}) { _input_view = std::make_unique(&_input_buffer, false); } @@ -34,7 +35,6 @@ void Terminal::add_history(const std::string& line) { _history.push_back(line_with_spaces); if(line == "__CLOSE_CONNECTION__") { - _history.back() = "Connection closed by server."; _should_close = true; } } @@ -43,25 +43,38 @@ void Terminal::set_prompt(const std::string& prompt) { _prompt = prompt; } -bool Terminal::should_close(void) { return _should_close; } +bool Terminal::should_close(void) { + return _should_close; +} WindowAction Terminal::get_pending_action(void) { - return { ActionType::NONE }; + WindowAction action = _pending_action; + _pending_action.type = ActionType::NONE; + return action; } void Terminal::_on_ret_press(void) { std::string command = _input_buffer.get_line(0); - _input_buffer.newline(); /* Add newline to buffer for histroy. */ _input_buffer.clear(); - /* Add the command to history. */ - _history.push_back(_prompt + "> " + command); - if(command == "clear") { + _history.push_back(_prompt + "> " + command); _history.clear(); return; } + /* Client-side command handling. */ + std::stringstream ss(command); + std::string cmd_name; + ss >> cmd_name; + if(cmd_name == "edit") { + _history.push_back(_prompt + "> " + command); + ss >> _pending_action.payload1; /* filename. */ + _pending_action.type = ActionType::READ_FILE; + return; + } + + _history.push_back(_prompt + "> " + command); _network->send(command); /* TODO: Ugly hack. Refactor to pass window height @@ -77,9 +90,7 @@ void Terminal::_on_ret_press(void) { void Terminal::handle_input(SDL_Event* event) { /* Pass input to TextView; if true, RET was pressed. */ - if(_input_view->handle_event(event)) { - _on_ret_press(); - } + if(_input_view->handle_event(event)) { _on_ret_press(); } } void Terminal::scroll(int amount, int win_content_height) { diff --git a/client/src/terminal.h b/client/src/terminal.h index bb77b3c..37812a1 100644 --- a/client/src/terminal.h +++ b/client/src/terminal.h @@ -9,6 +9,7 @@ #include "ui/text_buffer.h" #include "ui/text_view.h" #include "ui/i_window_content.h" +#include "ui/window_action.h" class Terminal : public IWindowContent { public: @@ -33,5 +34,6 @@ private: std::string _prompt; ClientNetwork* _network; TextBuffer _input_buffer; + WindowAction _pending_action; std::unique_ptr _input_view; }; diff --git a/client/src/ui/desktop.cpp b/client/src/ui/desktop.cpp index 5d1007e..0516a1d 100644 --- a/client/src/ui/desktop.cpp +++ b/client/src/ui/desktop.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include "ui/editor.h" @@ -158,6 +159,31 @@ void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height void Desktop::update(int screen_width, int screen_height) { /* Remove closed windows. */ + if(_focused_window) { + IWindowContent* content = _focused_window->get_content(); + if(content) { + WindowAction action = content->get_pending_action(); + switch(action.type) { + case ActionType::WRITE_FILE: { + std::string message = "WRITEF::" + action.payload1 + "::" + action.payload2; + _network->send(message); + break; + } + case ActionType::READ_FILE: { + std::string message = "READF::" + action.payload1; + _network->send(message); + break; + } + default: + break; + } + } + } + for(auto& window : _windows) { + if(window) { + window->update(); + } + } _update_wallpaper(screen_width, screen_height); _windows.erase(std::remove_if(_windows.begin(), _windows.end(), [this](const std::unique_ptr& w) { @@ -172,16 +198,6 @@ void Desktop::update(int screen_width, int screen_height) { _windows.end()); } -WindowAction Desktop::get_pending_action(void) { - if(_focused_window) { - IWindowContent* content = _focused_window->get_content(); - if(content) { - return content->get_pending_action(); - } - } - return { ActionType::NONE }; -} - UIWindow* Desktop::get_focused_window(void) { return _focused_window; } diff --git a/client/src/ui/desktop.h b/client/src/ui/desktop.h index ba56026..e01efb3 100644 --- a/client/src/ui/desktop.h +++ b/client/src/ui/desktop.h @@ -10,7 +10,6 @@ #include "ui/ui_window.h" #include "ui/taskbar.h" #include "ui/launcher.h" -#include "ui/window_action.h" /* Animated background stuff. */ struct ScrollingText { @@ -32,7 +31,6 @@ public: void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height, bool show_cursor); - WindowAction get_pending_action(void); UIWindow* get_focused_window(void); private: diff --git a/client/src/ui/editor.cpp b/client/src/ui/editor.cpp index 92a6d55..aaa7f63 100644 --- a/client/src/ui/editor.cpp +++ b/client/src/ui/editor.cpp @@ -38,6 +38,10 @@ bool Editor::should_close(void) { return _should_close; } +void Editor::set_buffer_content(const std::string& content) { + _buffer.set_text(content); +} + WindowAction Editor::get_pending_action(void) { WindowAction action = _pending_action; _pending_action.type = ActionType::NONE; /* Clear action. */ diff --git a/client/src/ui/editor.h b/client/src/ui/editor.h index 13666b6..9851668 100644 --- a/client/src/ui/editor.h +++ b/client/src/ui/editor.h @@ -19,6 +19,7 @@ public: bool show_cursor) override; void scroll(int amount, int content_height) override; bool should_close(void) override; + void set_buffer_content(const std::string& content); WindowAction get_pending_action(void) override; private: diff --git a/client/src/ui/ui_window.cpp b/client/src/ui/ui_window.cpp index d1ce9b3..5544b88 100644 --- a/client/src/ui/ui_window.cpp +++ b/client/src/ui/ui_window.cpp @@ -5,6 +5,7 @@ #include "gfx/txt_renderer.h" #include "gfx/types.h" #include "ui/i_window_content.h" +#include "ui/window_action.h" UIWindow::UIWindow(const char* title, int x, int y, int width, int height) : _title(title), @@ -54,6 +55,12 @@ bool UIWindow::should_close(void) const { return _should_close; } +void UIWindow::update(void) { + if(_content) { + if(_content->should_close()) { _should_close = true; } + } +} + void UIWindow::set_content(std::unique_ptr content) { _content = std::move(content); } diff --git a/client/src/ui/ui_window.h b/client/src/ui/ui_window.h index f70b362..0de0b8c 100644 --- a/client/src/ui/ui_window.h +++ b/client/src/ui/ui_window.h @@ -30,6 +30,7 @@ public: UIWindow(const char* title, int x, int y, int width, int height); ~UIWindow(void); + void update(void); void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height, bool show_cursor); void handle_event(SDL_Event* event, int screen_width, int screen_height, diff --git a/client/src/ui/window_action.h b/client/src/ui/window_action.h index 37e7b30..6ef47f8 100644 --- a/client/src/ui/window_action.h +++ b/client/src/ui/window_action.h @@ -4,7 +4,9 @@ enum class ActionType { NONE, - WRITE_FILE + WRITE_FILE, + READ_FILE, + CLOSE_WINDOW }; struct WindowAction { diff --git a/common/src/command_processor.cpp b/common/src/command_processor.cpp index c56d566..b09746a 100644 --- a/common/src/command_processor.cpp +++ b/common/src/command_processor.cpp @@ -103,6 +103,15 @@ vfs_node* find_node_by_path(vfs_node* root, const std::string& path) { return current; } +std::string CommandProcessor::read_file(const std::string& path) { + vfs_node* root = get_session_machine()->vfs_root; + vfs_node* node = find_node_by_path(root, path); + if(node && node->type == FILE_NODE) { + return node->content; + } + return "Error: file not found."; +} + void CommandProcessor::ensure_vfs_is_writable(void) { if(!_session_machine->is_vfs_a_copy) { /* VFS shared, copy required. */ diff --git a/common/src/command_processor.h b/common/src/command_processor.h index 5a0d6b3..5b3be66 100644 --- a/common/src/command_processor.h +++ b/common/src/command_processor.h @@ -14,6 +14,7 @@ public: std::string process_command(const std::string& command); std::string write_file(const std::string& path, const std::string& content); + std::string read_file(const std::string& path); /* Public interface for API functions. */ vfs_node* get_current_dir(void); diff --git a/common/src/ui/text_buffer.cpp b/common/src/ui/text_buffer.cpp index 3c81893..181826d 100644 --- a/common/src/ui/text_buffer.cpp +++ b/common/src/ui/text_buffer.cpp @@ -84,3 +84,14 @@ void TextBuffer::clear(void) { _cursor_row = 0; _cursor_col = 0; } + +void TextBuffer::set_text(const std::string& text) { + _lines.clear(); + std::stringstream ss(text); + std::string line; + while(std::getline(ss, line, '\n')) { + _lines.push_back(line); + } + _cursor_row = 0; + _cursor_col = 0; +} diff --git a/common/src/ui/text_buffer.h b/common/src/ui/text_buffer.h index e3eadad..eda99fe 100644 --- a/common/src/ui/text_buffer.h +++ b/common/src/ui/text_buffer.h @@ -25,6 +25,7 @@ public: Point get_cursor_pos(void) const; std::string get_text(void) const; void clear(void); + void set_text(const std::string& text); private: std::vector _lines; diff --git a/server/src/network_manager.cpp b/server/src/network_manager.cpp index 4750e12..80c40ce 100644 --- a/server/src/network_manager.cpp +++ b/server/src/network_manager.cpp @@ -122,6 +122,14 @@ void NetworkManager::on_message(std::shared_ptr connection, } return; } + if(message.rfind("READF::", 0) == 0) { + std::string filepath = message.substr(7); + fprintf(stderr, "[Player %u] Read file: '%s'\n", player->id, filepath.c_str()); + std::string content = player->cmd_processor->read_file(filepath); + /* Send the content back to the client. */ + connection->send("FILEDATA::" + filepath + "::" + content); + return; + } /* If no prefix, treat as normal terminal command. */ fprintf(stderr, "[Player %u] Command: '%s'\n", player->id, message.c_str());