parent
							
								
									b150c814fe
								
							
						
					
					
						commit
						651f7c415e
					
				@ -1,6 +1,7 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <chrono>
 | 
			
		||||
 | 
			
		||||
@ -25,9 +26,13 @@ void GameState::_init_desktop(void) {
 | 
			
		||||
  _desktop = std::make_unique<Desktop>(_screen_width, _screen_height, this); 
 | 
			
		||||
 | 
			
		||||
  auto term = std::make_unique<Terminal>(this);
 | 
			
		||||
  term->set_session_id(_initial_session_id);
 | 
			
		||||
 | 
			
		||||
  auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
 | 
			
		||||
  UIWindow* window_ptr = term_window.get();
 | 
			
		||||
  term_window->set_content(std::move(term));
 | 
			
		||||
  _desktop->add_window(std::move(term_window));
 | 
			
		||||
  _desktop->register_session(_initial_session_id, window_ptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameState::_run_server(void) {
 | 
			
		||||
@ -52,7 +57,8 @@ GameState::GameState(void) :
 | 
			
		||||
    _screen_width(0),
 | 
			
		||||
    _screen_height(0),
 | 
			
		||||
    _is_single_player(false),
 | 
			
		||||
    _show_debug_overlay(false) {_debug_overlay = std::make_unique<DebugOverlay>();}
 | 
			
		||||
    _show_debug_overlay(false),
 | 
			
		||||
    _initial_session_id(0) {_debug_overlay = std::make_unique<DebugOverlay>();}
 | 
			
		||||
 | 
			
		||||
GameState::~GameState(void) = default;
 | 
			
		||||
 | 
			
		||||
@ -170,6 +176,12 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
 | 
			
		||||
      switch(opcode) {
 | 
			
		||||
        case net_protocol::Opcode::S2C_LOGIN_SUCCESS:
 | 
			
		||||
        case net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS:
 | 
			
		||||
          /* Don't switch screen yet, wait for the session ID. */
 | 
			
		||||
          break;
 | 
			
		||||
        case net_protocol::Opcode::S2C_SESSION_CREATED:
 | 
			
		||||
          if(!args.empty()) {
 | 
			
		||||
            _initial_session_id = std::stoul(args[0]);
 | 
			
		||||
          }
 | 
			
		||||
          _current_screen = Screen::BOOTING;
 | 
			
		||||
          _login_screen.reset(); /* Free mem. */
 | 
			
		||||
          _boot_sequence = std::make_unique<BootSequence>();
 | 
			
		||||
@ -210,57 +222,79 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
 | 
			
		||||
      net_protocol::parse_message(server_msg, opcode, args);
 | 
			
		||||
 | 
			
		||||
      switch(opcode) {
 | 
			
		||||
        case net_protocol::Opcode::S2C_SESSION_CREATED:
 | 
			
		||||
          if(!args.empty()) {
 | 
			
		||||
            uint32_t session_id = std::stoul(args[0]);
 | 
			
		||||
            UIWindow* new_term = _desktop->get_window_awaiting_session_id();
 | 
			
		||||
            if(new_term) {
 | 
			
		||||
              Terminal* term = dynamic_cast<Terminal*>(new_term->get_content());
 | 
			
		||||
              if(term) {
 | 
			
		||||
                term->set_session_id(session_id);
 | 
			
		||||
                _desktop->register_session(session_id, new_term);
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case net_protocol::Opcode::S2C_FILE_DATA:
 | 
			
		||||
          if(args.size() == 2) {
 | 
			
		||||
            auto editor = std::make_unique<Editor>(args[0]);
 | 
			
		||||
            editor->set_buffer_content(args[1]);
 | 
			
		||||
            auto editor_window = std::make_unique<UIWindow>(args[0].c_str(),
 | 
			
		||||
          if(args.size() == 3) {
 | 
			
		||||
            uint32_t session_id = std::stoul(args[0]);
 | 
			
		||||
            auto editor = std::make_unique<Editor>(args[1]);
 | 
			
		||||
            editor->set_buffer_content(args[2]);
 | 
			
		||||
            auto editor_window = std::make_unique<UIWindow>(args[1].c_str(),
 | 
			
		||||
                                                            200, 200, 600, 400);
 | 
			
		||||
            editor_window->set_content(std::move(editor));
 | 
			
		||||
            _desktop->add_window(std::move(editor_window));
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        case net_protocol::Opcode::S2C_DISCONNECT: {
 | 
			
		||||
          IWindowContent* content = _desktop->get_focused_window() ?
 | 
			
		||||
              _desktop->get_focused_window()->get_content() : nullptr;
 | 
			
		||||
          Terminal* terminal = dynamic_cast<Terminal*>(content);
 | 
			
		||||
 | 
			
		||||
          if(terminal) {
 | 
			
		||||
            terminal->add_history("Connection closed.");
 | 
			
		||||
          if(!args.empty()) {
 | 
			
		||||
            uint32_t session_id = std::stoul(args[0]);
 | 
			
		||||
            UIWindow* window = _desktop->get_window_by_session_id(session_id);
 | 
			
		||||
            if(window) {
 | 
			
		||||
              Terminal* terminal = dynamic_cast<Terminal*>(window->get_content());
 | 
			
		||||
              if(terminal) {
 | 
			
		||||
                terminal->add_history("Connection closed.");
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
        case net_protocol::Opcode::S2C_CLOSE_WINDOW: {
 | 
			
		||||
          if(_desktop) {
 | 
			
		||||
            UIWindow* focused_window = _desktop->get_focused_window();
 | 
			
		||||
            if(focused_window) { focused_window->close(); }
 | 
			
		||||
          if(!args.empty()) {
 | 
			
		||||
            uint32_t session_id = std::stoul(args[0]);
 | 
			
		||||
            UIWindow* window = _desktop->get_window_by_session_id(session_id);
 | 
			
		||||
            if(window) {
 | 
			
		||||
              window->close();
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
        case net_protocol::Opcode::S2C_COMMAND_RESPONSE: {
 | 
			
		||||
          if(!args.empty()) {
 | 
			
		||||
            IWindowContent* content = _desktop->get_focused_window() ?
 | 
			
		||||
                _desktop->get_focused_window()->get_content() : nullptr;
 | 
			
		||||
            Terminal* terminal = dynamic_cast<Terminal*>(content);
 | 
			
		||||
            if(!terminal) continue;
 | 
			
		||||
          if(args.size() == 2) {
 | 
			
		||||
            uint32_t session_id = std::stoul(args[0]);
 | 
			
		||||
            UIWindow* window = _desktop->get_window_by_session_id(session_id);
 | 
			
		||||
            if(window) {
 | 
			
		||||
              Terminal* terminal = dynamic_cast<Terminal*>(window->get_content());
 | 
			
		||||
              if(terminal) {
 | 
			
		||||
                /* Server sends "output\nprompt", split them. */
 | 
			
		||||
                size_t last_newline = args[1].find_last_of('\n');
 | 
			
		||||
                if(last_newline != std::string::npos) {
 | 
			
		||||
                  std::string prompt = args[1].substr(last_newline+1);
 | 
			
		||||
                  terminal->set_prompt(prompt);
 | 
			
		||||
 | 
			
		||||
            /* Server sends "output\nprompt", split them. */
 | 
			
		||||
            size_t last_newline = args[0].find_last_of('\n');
 | 
			
		||||
            if(last_newline != std::string::npos) {
 | 
			
		||||
              std::string prompt = args[0].substr(last_newline+1);
 | 
			
		||||
              terminal->set_prompt(prompt);
 | 
			
		||||
 | 
			
		||||
              std::string output = args[0].substr(0, last_newline);
 | 
			
		||||
              if(!output.empty()) {
 | 
			
		||||
                /* Split multiline output and push each line to history. */
 | 
			
		||||
                std::stringstream ss(output);
 | 
			
		||||
                std::string line;
 | 
			
		||||
                while(std::getline(ss, line, '\n')) {
 | 
			
		||||
                  terminal->add_history(line);
 | 
			
		||||
                  std::string output = args[1].substr(0, last_newline);
 | 
			
		||||
                  if(!output.empty()) {
 | 
			
		||||
                    /* Split multiline output and push each line to history. */
 | 
			
		||||
                    std::stringstream ss(output);
 | 
			
		||||
                    std::string line;
 | 
			
		||||
                    while(std::getline(ss, line, '\n')) {
 | 
			
		||||
                      terminal->add_history(line);
 | 
			
		||||
                    }
 | 
			
		||||
                  }
 | 
			
		||||
                } else {
 | 
			
		||||
                  terminal->add_history(args[1]);
 | 
			
		||||
                }
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              terminal->add_history(args[0]);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
@ -275,13 +309,6 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameState::send_network_command(const std::string& command) {
 | 
			
		||||
  if(_network && _network->is_connected()) {
 | 
			
		||||
    _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_COMMAND,
 | 
			
		||||
                                               {command}));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameState::render(const RenderContext& context) {
 | 
			
		||||
  switch(_current_screen) {
 | 
			
		||||
  case Screen::MAIN_MENU:
 | 
			
		||||
@ -311,12 +338,26 @@ void GameState::render(const RenderContext& context) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameState::send_file_write_request(const std::string& path, const std::string& content) {
 | 
			
		||||
  _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_WRITE_FILE,
 | 
			
		||||
                                             {path, content}));
 | 
			
		||||
void GameState::send_network_command(uint32_t session_id, const std::string& command) {
 | 
			
		||||
  if(_network && _network->is_connected()) {
 | 
			
		||||
    _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_COMMAND,
 | 
			
		||||
                                               {std::to_string(session_id), command}));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameState::send_file_read_request(const std::string& path) {
 | 
			
		||||
  _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_READ_FILE,
 | 
			
		||||
                                             {path}));
 | 
			
		||||
void GameState::send_file_write_request(uint32_t session_id, const std::string& path,
 | 
			
		||||
                                        const std::string& content) {
 | 
			
		||||
  _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_WRITE_FILE,
 | 
			
		||||
                                             {std::to_string(session_id), path, content}));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameState::send_file_read_request(uint32_t session_id, const std::string& path) {
 | 
			
		||||
  _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_READ_FILE,
 | 
			
		||||
                                             {std::to_string(session_id), path}));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameState::send_create_session_request(void) {
 | 
			
		||||
  if(_network && _network->is_connected()) {
 | 
			
		||||
    _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_CREATE_SESSION));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -33,9 +33,10 @@ public:
 | 
			
		||||
  void render(const RenderContext& context);
 | 
			
		||||
 | 
			
		||||
  /* Public network interface for UI components. */
 | 
			
		||||
  void send_network_command(const std::string& command);
 | 
			
		||||
  void send_file_write_request(const std::string& path, const std::string& content);
 | 
			
		||||
  void send_file_read_request(const std::string& path);
 | 
			
		||||
  void send_network_command(uint32_t session_id, const std::string& command);
 | 
			
		||||
  void send_file_write_request(uint32_t session_id, const std::string& path, const std::string& content);
 | 
			
		||||
  void send_file_read_request(uint32_t session_id, const std::string& path);
 | 
			
		||||
  void send_create_session_request(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  std::unique_ptr<ClientNetwork>  _network;
 | 
			
		||||
@ -49,8 +50,8 @@ private:
 | 
			
		||||
  int                             _screen_width;
 | 
			
		||||
  int                             _screen_height;
 | 
			
		||||
  bool                            _is_single_player;
 | 
			
		||||
  uint32_t                        _initial_session_id;
 | 
			
		||||
 | 
			
		||||
  void _init_desktop(void);
 | 
			
		||||
  void _send_network_command(const std::string& command);
 | 
			
		||||
  void _run_server(void);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,8 @@
 | 
			
		||||
 | 
			
		||||
Terminal::Terminal(GameState* game_state)
 | 
			
		||||
    : _game_state(game_state), _should_close(false), _command_history_index(0),
 | 
			
		||||
      _scroll_offset(0), _prompt(""), _pending_action({ActionType::NONE}) {
 | 
			
		||||
      _scroll_offset(0), _prompt(""), _pending_action({ActionType::NONE}),
 | 
			
		||||
      _session_id(0) {
 | 
			
		||||
  _input_view = std::make_unique<TextView>(&_input_buffer, false, false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -52,6 +53,14 @@ WindowAction Terminal::get_pending_action(void) {
 | 
			
		||||
  return action;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Terminal::set_session_id(uint32_t id) {
 | 
			
		||||
  _session_id = id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uint32_t Terminal::get_session_id(void) const {
 | 
			
		||||
  return _session_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Terminal::_on_ret_press(void) {
 | 
			
		||||
  std::string command = _input_buffer.get_line(0);
 | 
			
		||||
  if(!command.empty()) {
 | 
			
		||||
@ -79,7 +88,7 @@ void Terminal::_on_ret_press(void) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _history.push_back(_prompt + "> " + command);
 | 
			
		||||
  _game_state->send_network_command(command);
 | 
			
		||||
  _game_state->send_network_command(_session_id, command);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Terminal::handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) {
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,8 @@ public:
 | 
			
		||||
  void set_prompt(const std::string& prompt);
 | 
			
		||||
  bool should_close(void) override;
 | 
			
		||||
  WindowAction get_pending_action(void) override;
 | 
			
		||||
  void set_session_id(uint32_t id);
 | 
			
		||||
  uint32_t get_session_id(void) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void _on_ret_press(void);
 | 
			
		||||
@ -39,5 +41,6 @@ private:
 | 
			
		||||
  GameState* _game_state;
 | 
			
		||||
  TextBuffer _input_buffer;
 | 
			
		||||
  WindowAction _pending_action;
 | 
			
		||||
  uint32_t _session_id;
 | 
			
		||||
  std::unique_ptr<TextView> _input_view;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ Desktop::Desktop(int screen_width, int screen_height, GameState* game_state) {
 | 
			
		||||
  _taskbar = std::make_unique<Taskbar>(screen_width, screen_height);
 | 
			
		||||
  _game_state= game_state;
 | 
			
		||||
  _focused_window = nullptr;
 | 
			
		||||
  _window_awaiting_session_id = nullptr;
 | 
			
		||||
  _launcher_is_open = false;
 | 
			
		||||
  _launcher = std::make_unique<Launcher>(5, 0, 200); /* Tmp y-coord. */
 | 
			
		||||
  int launcher_y = screen_height - _taskbar->get_height() - _launcher->get_height();
 | 
			
		||||
@ -135,9 +136,11 @@ void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height
 | 
			
		||||
      if(app_to_launch == "Terminal") {
 | 
			
		||||
        auto term = std::make_unique<Terminal>(_game_state);
 | 
			
		||||
        auto term_window = std::make_unique<UIWindow>("Terminal", 150, 150, 800, 500);
 | 
			
		||||
        _window_awaiting_session_id = term_window.get();
 | 
			
		||||
        term_window->set_content(std::move(term));
 | 
			
		||||
        add_window(std::move(term_window));
 | 
			
		||||
        _launcher_is_open = false;
 | 
			
		||||
        _game_state->send_create_session_request();
 | 
			
		||||
      } else if(app_to_launch == "Editor") {
 | 
			
		||||
        auto editor = std::make_unique<Editor>();
 | 
			
		||||
        auto editor_window = std::make_unique<UIWindow>("Editor", 200, 200, 600, 400);
 | 
			
		||||
@ -180,13 +183,21 @@ void Desktop::update(float dt, int screen_width, int screen_height) {
 | 
			
		||||
    IWindowContent* content = _focused_window->get_content();
 | 
			
		||||
    if(content) {
 | 
			
		||||
      WindowAction action = content->get_pending_action();
 | 
			
		||||
      uint32_t session_id = 0;
 | 
			
		||||
      if(auto term = dynamic_cast<Terminal*>(content)) {
 | 
			
		||||
        session_id = term->get_session_id();
 | 
			
		||||
      }
 | 
			
		||||
      switch(action.type) {
 | 
			
		||||
        case ActionType::WRITE_FILE: {
 | 
			
		||||
          _game_state->send_file_write_request(action.payload1, action.payload2);
 | 
			
		||||
          if(session_id != 0) {
 | 
			
		||||
            _game_state->send_file_write_request(session_id, action.payload1, action.payload2);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        case ActionType::READ_FILE: {
 | 
			
		||||
          _game_state->send_file_read_request(action.payload1);
 | 
			
		||||
          if(session_id != 0) {
 | 
			
		||||
            _game_state->send_file_read_request(session_id, action.payload1);
 | 
			
		||||
          }
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        default:
 | 
			
		||||
@ -203,6 +214,16 @@ void Desktop::update(float dt, int screen_width, int screen_height) {
 | 
			
		||||
  _windows.erase(std::remove_if(_windows.begin(), _windows.end(),
 | 
			
		||||
                                [this](const std::unique_ptr<UIWindow>& w) {
 | 
			
		||||
                                  if (w->should_close()) {
 | 
			
		||||
                                    /* Also remove from session map. */
 | 
			
		||||
                                    uint32_t session_to_remove = 0;
 | 
			
		||||
                                    for(auto const& [sid, win] : _session_windows) {
 | 
			
		||||
                                      if(win == w.get()) {
 | 
			
		||||
                                        session_to_remove = sid;
 | 
			
		||||
                                        break;
 | 
			
		||||
                                      }
 | 
			
		||||
                                    }
 | 
			
		||||
                                    if(session_to_remove != 0)
 | 
			
		||||
                                      _session_windows.erase(session_to_remove);
 | 
			
		||||
                                    _taskbar->remove_window(w.get());
 | 
			
		||||
                                    if (w.get() == _focused_window) {
 | 
			
		||||
                                      _focused_window = nullptr;
 | 
			
		||||
@ -214,10 +235,28 @@ void Desktop::update(float dt, int screen_width, int screen_height) {
 | 
			
		||||
                 _windows.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::register_session(uint32_t session_id, UIWindow* window) {
 | 
			
		||||
  _session_windows[session_id] = window;
 | 
			
		||||
  if(_window_awaiting_session_id == window) {
 | 
			
		||||
    _window_awaiting_session_id = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UIWindow* Desktop::get_window_by_session_id(uint32_t session_id) {
 | 
			
		||||
  if(_session_windows.count(session_id)) {
 | 
			
		||||
    return _session_windows.at(session_id);
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UIWindow* Desktop::get_focused_window(void) {
 | 
			
		||||
  return _focused_window;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UIWindow* Desktop::get_window_awaiting_session_id(void) {
 | 
			
		||||
  return _window_awaiting_session_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::render(const RenderContext& context) {
 | 
			
		||||
  /* Pass 1: Background. */
 | 
			
		||||
  context.ui_renderer->begin_text();
 | 
			
		||||
 | 
			
		||||
@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <SDL3/SDL.h>
 | 
			
		||||
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
@ -31,7 +32,10 @@ public:
 | 
			
		||||
  void update(float dt, int screen_width, int screen_height);
 | 
			
		||||
  void render(const RenderContext& context);
 | 
			
		||||
 | 
			
		||||
  void register_session(uint32_t session_id, UIWindow* window);
 | 
			
		||||
  UIWindow* get_window_by_session_id(uint32_t session_id);
 | 
			
		||||
  UIWindow* get_focused_window(void);
 | 
			
		||||
  UIWindow* get_window_awaiting_session_id(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void _set_focused_window(UIWindow* window);
 | 
			
		||||
@ -39,11 +43,13 @@ private:
 | 
			
		||||
  void _update_wallpaper(float dt, int screen_width, int screen_height);
 | 
			
		||||
 | 
			
		||||
  std::vector<std::unique_ptr<UIWindow>> _windows;
 | 
			
		||||
  std::unique_ptr<Taskbar>   _taskbar;
 | 
			
		||||
  std::unique_ptr<Launcher>  _launcher;
 | 
			
		||||
  UIWindow*                  _focused_window;
 | 
			
		||||
  GameState*                 _game_state;
 | 
			
		||||
  std::vector<ScrollingText> _background_text;
 | 
			
		||||
  std::vector<std::string>   _snippets;
 | 
			
		||||
  bool                       _launcher_is_open;
 | 
			
		||||
  std::unique_ptr<Taskbar>      _taskbar;
 | 
			
		||||
  std::unique_ptr<Launcher>     _launcher;
 | 
			
		||||
  UIWindow*                     _focused_window;
 | 
			
		||||
  UIWindow*                     _window_awaiting_session_id;
 | 
			
		||||
  std::map<uint32_t, UIWindow*> _session_windows;
 | 
			
		||||
  GameState*                    _game_state;
 | 
			
		||||
  std::vector<ScrollingText>    _background_text;
 | 
			
		||||
  std::vector<std::string>      _snippets;
 | 
			
		||||
  bool                          _launcher_is_open;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -4,17 +4,17 @@
 | 
			
		||||
 | 
			
		||||
#include "i_network_bridge.h"
 | 
			
		||||
#include "lua_api.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "session.h"
 | 
			
		||||
#include "machine.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
vfs_node* get_current_dir(CommandProcessor& context) {
 | 
			
		||||
vfs_node* get_current_dir(Session& context) {
 | 
			
		||||
  return context.get_current_dir();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string rm(CommandProcessor& context, const std::string& filename) {
 | 
			
		||||
std::string rm(Session& context, const std::string& filename) {
 | 
			
		||||
  vfs_node* current_dir = context.get_current_dir();
 | 
			
		||||
  auto it = current_dir->children.find(filename);
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ std::string rm(CommandProcessor& context, const std::string& filename) {
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string write_file(CommandProcessor& context, const std::string& filename,
 | 
			
		||||
std::string write_file(Session& context, const std::string& filename,
 | 
			
		||||
                       const std::string& content) {
 | 
			
		||||
  vfs_node* current_dir = context.get_current_dir();
 | 
			
		||||
  auto it = current_dir->children.find(filename);
 | 
			
		||||
@ -50,7 +50,7 @@ std::string write_file(CommandProcessor& context, const std::string& filename,
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string cd(CommandProcessor& context, const std::string& path) {
 | 
			
		||||
std::string cd(Session& context, const std::string& path) {
 | 
			
		||||
  vfs_node* current_dir = context.get_current_dir();
 | 
			
		||||
 | 
			
		||||
  if(path == "..") {
 | 
			
		||||
@ -68,7 +68,7 @@ std::string cd(CommandProcessor& context, const std::string& path) {
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ls(CommandProcessor& context) {
 | 
			
		||||
std::string ls(Session& context) {
 | 
			
		||||
  vfs_node* dir = context.get_current_dir();
 | 
			
		||||
  if(dir->type != DIR_NODE) {
 | 
			
		||||
    return "ls: not a directory";
 | 
			
		||||
@ -85,7 +85,7 @@ std::string ls(CommandProcessor& context) {
 | 
			
		||||
  return ss.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ssh(CommandProcessor& context, const std::string& ip) {
 | 
			
		||||
std::string ssh(Session& context, const std::string& ip) {
 | 
			
		||||
  INetworkBridge* bridge = context.get_network_bridge();
 | 
			
		||||
  Machine* target_machine = bridge->get_machine_by_ip(ip);
 | 
			
		||||
 | 
			
		||||
@ -97,7 +97,7 @@ std::string ssh(CommandProcessor& context, const std::string& ip) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string nmap(CommandProcessor& context, const std::string& ip) {
 | 
			
		||||
std::string nmap(Session& context, const std::string& ip) {
 | 
			
		||||
  long long machine_id = context.get_machine_manager()->get_machine_id_by_ip(ip);
 | 
			
		||||
  if(machine_id == 0) {
 | 
			
		||||
    return "nmap: Could not resolve host: " + ip;
 | 
			
		||||
@ -117,7 +117,7 @@ std::string nmap(CommandProcessor& context, const std::string& ip) {
 | 
			
		||||
  return ss.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string disconnect(CommandProcessor& context) {
 | 
			
		||||
std::string disconnect(Session& context) {
 | 
			
		||||
  Machine* current_machine = context.get_session_machine();
 | 
			
		||||
  if(current_machine != context.get_home_machine()) {
 | 
			
		||||
    INetworkBridge* bridge = context.get_network_bridge();
 | 
			
		||||
@ -127,7 +127,7 @@ std::string disconnect(CommandProcessor& context) {
 | 
			
		||||
  return "Connection closed.";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string close_terminal(CommandProcessor& context) {
 | 
			
		||||
std::string close_terminal(Session& context) {
 | 
			
		||||
  return "__CLOSE_CONNECTION__";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -145,7 +145,7 @@ ScpPath parse_scp_path(const std::string& arg) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string scp(CommandProcessor& context, const std::string& source_arg,
 | 
			
		||||
std::string scp(Session& context, const std::string& source_arg,
 | 
			
		||||
                const std::string& dest_arg) {
 | 
			
		||||
  ScpPath source_path = parse_scp_path(source_arg);
 | 
			
		||||
  ScpPath dest_path = parse_scp_path(dest_arg);
 | 
			
		||||
 | 
			
		||||
@ -3,27 +3,27 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
class CommandProcessor;
 | 
			
		||||
class Session;
 | 
			
		||||
class NetworkManager;
 | 
			
		||||
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
/* FILESYSTEM ACTIONS. */
 | 
			
		||||
vfs_node* get_current_dir(CommandProcessor& context);
 | 
			
		||||
std::string rm(CommandProcessor& context, const std::string& filename);
 | 
			
		||||
std::string write_file(CommandProcessor& context, const std::string& filename,
 | 
			
		||||
vfs_node* get_current_dir(Session& context);
 | 
			
		||||
std::string rm(Session& context, const std::string& filename);
 | 
			
		||||
std::string write_file(Session& context, const std::string& filename,
 | 
			
		||||
                       const std::string& content);
 | 
			
		||||
std::string ls(CommandProcessor& context);
 | 
			
		||||
std::string cd(CommandProcessor& context, const std::string& path);
 | 
			
		||||
std::string scp(CommandProcessor& context, const std::string& source,
 | 
			
		||||
std::string ls(Session& context);
 | 
			
		||||
std::string cd(Session& context, const std::string& path);
 | 
			
		||||
std::string scp(Session& context, const std::string& source,
 | 
			
		||||
                const std::string& destination);
 | 
			
		||||
 | 
			
		||||
/* NETWORK ACTIONS. */
 | 
			
		||||
std::string ssh(CommandProcessor& context, const std::string& ip);
 | 
			
		||||
std::string nmap(CommandProcessor& context, const std::string& ip);
 | 
			
		||||
std::string disconnect(CommandProcessor& context);
 | 
			
		||||
std::string ssh(Session& context, const std::string& ip);
 | 
			
		||||
std::string nmap(Session& context, const std::string& ip);
 | 
			
		||||
std::string disconnect(Session& context);
 | 
			
		||||
 | 
			
		||||
/* SYSTEM ACTIONS. */
 | 
			
		||||
std::string close_terminal(CommandProcessor& context);
 | 
			
		||||
std::string close_terminal(Session& context);
 | 
			
		||||
 | 
			
		||||
} /* namespace api */
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,10 @@
 | 
			
		||||
 | 
			
		||||
#include "lua_processor.h"
 | 
			
		||||
#include "lua_api.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "session.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
LuaProcessor::LuaProcessor(CommandProcessor& context) {
 | 
			
		||||
LuaProcessor::LuaProcessor(Session& context) {
 | 
			
		||||
  _lua.open_libraries(sol::lib::base, sol::lib::string, sol::lib::io, sol::lib::table);
 | 
			
		||||
 | 
			
		||||
  /* Expose vfs_node struct members to Lua. */
 | 
			
		||||
@ -19,7 +19,7 @@ LuaProcessor::LuaProcessor(CommandProcessor& context) {
 | 
			
		||||
                              "content",    &vfs_node::content);
 | 
			
		||||
 | 
			
		||||
  /* Expose CommandProcessor to Lua. DON'T ALLOW SCRIPTS TO CREATE IT THOUGH! */
 | 
			
		||||
  _lua.new_usertype<CommandProcessor>("CommandProcessor", sol::no_constructor);
 | 
			
		||||
  _lua.new_usertype<Session>("Session", sol::no_constructor);
 | 
			
		||||
 | 
			
		||||
  /* Create the 'bettola' API table. */
 | 
			
		||||
  sol::table bettola_api = _lua.create_named_table("bettola");
 | 
			
		||||
@ -37,7 +37,7 @@ LuaProcessor::LuaProcessor(CommandProcessor& context) {
 | 
			
		||||
 | 
			
		||||
LuaProcessor::~LuaProcessor(void) {}
 | 
			
		||||
 | 
			
		||||
sol::object LuaProcessor::execute(const std::string& script, CommandProcessor& context,
 | 
			
		||||
sol::object LuaProcessor::execute(const std::string& script, Session& context,
 | 
			
		||||
                                  const std::vector<std::string>& args, bool is_remote) {
 | 
			
		||||
  try {
 | 
			
		||||
    /* Pass C++ objects/points into the Lua env. */
 | 
			
		||||
 | 
			
		||||
@ -5,15 +5,15 @@
 | 
			
		||||
#include <vfs.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
class CommandProcessor;
 | 
			
		||||
class Session;
 | 
			
		||||
 | 
			
		||||
class LuaProcessor {
 | 
			
		||||
public:
 | 
			
		||||
  LuaProcessor(CommandProcessor& context);
 | 
			
		||||
  LuaProcessor(Session& context);
 | 
			
		||||
  ~LuaProcessor(void);
 | 
			
		||||
 | 
			
		||||
  /* Executes a string of lua code and returns result as a string. */
 | 
			
		||||
  sol::object execute(const std::string& script, CommandProcessor& context,
 | 
			
		||||
  sol::object execute(const std::string& script, Session& context,
 | 
			
		||||
                      const std::vector<std::string>& args, bool is_remote);
 | 
			
		||||
private:
 | 
			
		||||
  sol::state _lua;
 | 
			
		||||
 | 
			
		||||
@ -16,19 +16,21 @@ enum class Opcode : uint8_t {
 | 
			
		||||
  /* Client -> Server messages. */
 | 
			
		||||
  C2S_CREATE_ACCOUNT,
 | 
			
		||||
  C2S_LOGIN,
 | 
			
		||||
  C2S_COMMAND,
 | 
			
		||||
  C2S_WRITE_FILE,
 | 
			
		||||
  C2S_READ_FILE,
 | 
			
		||||
  C2S_CREATE_SESSION,
 | 
			
		||||
  C2S_COMMAND,    /* args: [session_id, command] */
 | 
			
		||||
  C2S_WRITE_FILE, /* args: [session_id, path, content] */
 | 
			
		||||
  C2S_READ_FILE,  /* args: [session_id, path] */
 | 
			
		||||
 | 
			
		||||
  /* Server -> Client messages. */
 | 
			
		||||
  S2C_CREATE_ACCOUNT_SUCCESS,
 | 
			
		||||
  S2C_CREATE_ACCOUNT_FAIL,
 | 
			
		||||
  S2C_LOGIN_SUCCESS,
 | 
			
		||||
  S2C_LOGIN_FAIL,
 | 
			
		||||
  S2C_COMMAND_RESPONSE,
 | 
			
		||||
  S2C_FILE_DATA,
 | 
			
		||||
  S2C_DISCONNECT,
 | 
			
		||||
  S2C_CLOSE_WINDOW,
 | 
			
		||||
  S2C_SESSION_CREATED,    /* args: [session_id] */
 | 
			
		||||
  S2C_COMMAND_RESPONSE,   /* args: [session_id, response] */
 | 
			
		||||
  S2C_FILE_DATA,          /* args: [session_id, path, content] */
 | 
			
		||||
  S2C_DISCONNECT,         /* args: [session_id] */
 | 
			
		||||
  S2C_CLOSE_WINDOW,       /* args: [session_id] */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
#include <sol/types.hpp>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "session.h"
 | 
			
		||||
#include "db/database_manager.h"
 | 
			
		||||
#include "i_network_bridge.h"
 | 
			
		||||
#include "lua_api.h"
 | 
			
		||||
@ -22,10 +22,10 @@ vfs_node* find_node_by_id(vfs_node* root, long long id) {
 | 
			
		||||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CommandProcessor::CommandProcessor(Machine* home_machine,
 | 
			
		||||
                                   DatabaseManager* db_manager,
 | 
			
		||||
                                   MachineManager* machine_manager,
 | 
			
		||||
                                   INetworkBridge* network_bridge) :
 | 
			
		||||
Session::Session(Machine* home_machine,
 | 
			
		||||
                 DatabaseManager* db_manager,
 | 
			
		||||
                 MachineManager* machine_manager,
 | 
			
		||||
                 INetworkBridge* network_bridge) :
 | 
			
		||||
    _machine_manager(machine_manager),
 | 
			
		||||
    _db_manager(db_manager),
 | 
			
		||||
    _network_bridge(network_bridge),
 | 
			
		||||
@ -35,40 +35,40 @@ CommandProcessor::CommandProcessor(Machine* home_machine,
 | 
			
		||||
  if(home_machine) _current_dir = home_machine->vfs_root;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CommandProcessor::~CommandProcessor(void) {
 | 
			
		||||
Session::~Session(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vfs_node* CommandProcessor::get_current_dir(void) {
 | 
			
		||||
vfs_node* Session::get_current_dir(void) {
 | 
			
		||||
  return _current_dir;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Machine* CommandProcessor::get_home_machine(void)     { return _home_machine; }
 | 
			
		||||
Machine* CommandProcessor::get_session_machine(void)  { return _session_machine; }
 | 
			
		||||
Machine* Session::get_home_machine(void)     { return _home_machine; }
 | 
			
		||||
Machine* Session::get_session_machine(void)  { return _session_machine; }
 | 
			
		||||
 | 
			
		||||
DatabaseManager* CommandProcessor::get_db_manager(void) {
 | 
			
		||||
DatabaseManager* Session::get_db_manager(void) {
 | 
			
		||||
  return _db_manager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MachineManager* CommandProcessor::get_machine_manager(void) {
 | 
			
		||||
MachineManager* Session::get_machine_manager(void) {
 | 
			
		||||
  return _machine_manager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
INetworkBridge* CommandProcessor::get_network_bridge(void) {
 | 
			
		||||
INetworkBridge* Session::get_network_bridge(void) {
 | 
			
		||||
  return _network_bridge;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandProcessor::set_current_dir(vfs_node* node) {
 | 
			
		||||
void Session::set_current_dir(vfs_node* node) {
 | 
			
		||||
  _current_dir = node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandProcessor::set_session_machine(Machine* machine) {
 | 
			
		||||
void Session::set_session_machine(Machine* machine) {
 | 
			
		||||
  _session_machine = machine;
 | 
			
		||||
  if(_session_machine) {
 | 
			
		||||
    _current_dir = _session_machine->vfs_root;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CommandProcessor::process_command(const std::string& command) {
 | 
			
		||||
std::string Session::process_command(const std::string& command) {
 | 
			
		||||
  /*
 | 
			
		||||
   * Creating the Lua processor on-demand to ensure it exists on the same
 | 
			
		||||
   * thread that will execute the script.
 | 
			
		||||
@ -113,11 +113,11 @@ std::string CommandProcessor::process_command(const std::string& command) {
 | 
			
		||||
  return "Unknown command: " + command_name + "\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CommandProcessor::write_file(const std::string& path, const std::string& content) {
 | 
			
		||||
std::string Session::write_file(const std::string& path, const std::string& content) {
 | 
			
		||||
  return api::write_file(*this, path, content);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CommandProcessor::read_file(const std::string& path) {
 | 
			
		||||
std::string Session::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) {
 | 
			
		||||
@ -9,11 +9,11 @@
 | 
			
		||||
 | 
			
		||||
class LuaProcessor;
 | 
			
		||||
 | 
			
		||||
class CommandProcessor {
 | 
			
		||||
class Session {
 | 
			
		||||
public:
 | 
			
		||||
  CommandProcessor(Machine* home_machine, DatabaseManager* db_manager,
 | 
			
		||||
  Session(Machine* home_machine, DatabaseManager* db_manager,
 | 
			
		||||
                   MachineManager* machine_manager, INetworkBridge* network_bridge);
 | 
			
		||||
  ~CommandProcessor(void);
 | 
			
		||||
  ~Session(void);
 | 
			
		||||
 | 
			
		||||
  std::string process_command(const std::string& command);
 | 
			
		||||
  std::string write_file(const std::string& path, const std::string& content);
 | 
			
		||||
@ -2,6 +2,7 @@
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "network_manager.h"
 | 
			
		||||
#include "net/message_protocol.h"
 | 
			
		||||
@ -9,7 +10,7 @@
 | 
			
		||||
 | 
			
		||||
#include "asio/error_code.hpp"
 | 
			
		||||
#include "asio/ip/tcp.hpp"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "session.h"
 | 
			
		||||
#include "player.h"
 | 
			
		||||
#include "machine.h"
 | 
			
		||||
#include "net/tcp_connection.h"
 | 
			
		||||
@ -112,37 +113,42 @@ void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
 | 
			
		||||
        if(args.size() == 3) {
 | 
			
		||||
          if(_db_manager->create_player(args[0], args[1], args[2],
 | 
			
		||||
                                      _machine_manager.get_vfs_template())) {
 | 
			
		||||
          long long home_machine_id = _db_manager->players().get_home_machine_id(args[0]);
 | 
			
		||||
          Machine* home_machine = _get_or_load_machine(home_machine_id);
 | 
			
		||||
          delete player->cmd_processor; /* Delete old command processor. */
 | 
			
		||||
          player->cmd_processor = new CommandProcessor(home_machine, _db_manager.get(),
 | 
			
		||||
                                                       &_machine_manager, this);
 | 
			
		||||
          player->state = PlayerState::ACTIVE;
 | 
			
		||||
          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS));
 | 
			
		||||
          /* send initial prompt. */
 | 
			
		||||
          std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
 | 
			
		||||
          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
 | 
			
		||||
                                                       {prompt}));
 | 
			
		||||
        } else {
 | 
			
		||||
          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_FAIL,
 | 
			
		||||
                                                       {"Username already exists."}));
 | 
			
		||||
            long long home_machine_id = _db_manager->players().get_home_machine_id(args[0]);
 | 
			
		||||
            Machine* home_machine = _get_or_load_machine(home_machine_id);
 | 
			
		||||
            player->state = PlayerState::ACTIVE;
 | 
			
		||||
 | 
			
		||||
            /* Create first session for player. */
 | 
			
		||||
            uint32_t session_id = _next_session_id++;
 | 
			
		||||
            player->sessions[session_id] = std::make_unique<Session>(home_machine,
 | 
			
		||||
                                                                     _db_manager.get(),
 | 
			
		||||
                                                                     &_machine_manager, this);
 | 
			
		||||
 | 
			
		||||
            connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS));
 | 
			
		||||
            connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_SESSION_CREATED,
 | 
			
		||||
                                                         {std::to_string(session_id)}));
 | 
			
		||||
          } else {
 | 
			
		||||
            connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_FAIL,
 | 
			
		||||
                                                          {"Username already exists."}));
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
        break;
 | 
			
		||||
      case net_protocol::Opcode::C2S_LOGIN:
 | 
			
		||||
        if(args.size() == 2) {
 | 
			
		||||
          if(_db_manager->players().authenticate(args[0], args[1])) {
 | 
			
		||||
            long long home_machine_id = _db_manager->players().get_home_machine_id(args[0]);
 | 
			
		||||
            Machine* home_machine = _get_or_load_machine(home_machine_id);
 | 
			
		||||
            delete player->cmd_processor; /* Delete old command processor. */
 | 
			
		||||
            player->cmd_processor = new CommandProcessor(home_machine, _db_manager.get(),
 | 
			
		||||
                                                         &_machine_manager, this);
 | 
			
		||||
            player->state = PlayerState::ACTIVE;
 | 
			
		||||
 | 
			
		||||
            /* Create first session for player. */
 | 
			
		||||
            uint32_t session_id = _next_session_id++;
 | 
			
		||||
            player->sessions[session_id] = std::make_unique<Session>(home_machine,
 | 
			
		||||
                                                                     _db_manager.get(),
 | 
			
		||||
                                                                     &_machine_manager, this);
 | 
			
		||||
 | 
			
		||||
            connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_LOGIN_SUCCESS));
 | 
			
		||||
            /* Send initial prompt. */
 | 
			
		||||
            std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
 | 
			
		||||
            connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
 | 
			
		||||
                                                        {prompt}));
 | 
			
		||||
            connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_SESSION_CREATED,
 | 
			
		||||
                                                         {std::to_string(session_id)}));
 | 
			
		||||
 | 
			
		||||
          } else {
 | 
			
		||||
            connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_LOGIN_FAIL,
 | 
			
		||||
                                                        {"Invalid username or password."}));
 | 
			
		||||
@ -159,36 +165,49 @@ void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
 | 
			
		||||
  /* === PLAYER BECOMES ACTIVE HERE === */
 | 
			
		||||
 | 
			
		||||
  switch(opcode) {
 | 
			
		||||
    case net_protocol::Opcode::C2S_CREATE_SESSION: {
 | 
			
		||||
      uint32_t session_id = _next_session_id++;
 | 
			
		||||
      /* Find player's home machine to init the session. */
 | 
			
		||||
      Machine* home_machine = nullptr;
 | 
			
		||||
      if(!player->sessions.empty()) {
 | 
			
		||||
        home_machine = player->sessions.begin()->second->get_home_machine();
 | 
			
		||||
      }
 | 
			
		||||
      player->sessions[session_id] = std::make_unique<Session>(home_machine,
 | 
			
		||||
                                                              _db_manager.get(),
 | 
			
		||||
                                                              &_machine_manager, this);
 | 
			
		||||
      connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_SESSION_CREATED,
 | 
			
		||||
                       {std::to_string(session_id)}));
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    case net_protocol::Opcode::C2S_WRITE_FILE:
 | 
			
		||||
      if(args.size() == 2) {
 | 
			
		||||
        fprintf(stderr, "[Player %u] Write file: '%s'\n", player->id, args[0].c_str());
 | 
			
		||||
        player->cmd_processor->write_file(args[0], args[1]);
 | 
			
		||||
      /* Response not required for a file write. */
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
      if(args.size() == 3) {
 | 
			
		||||
        uint32_t session_id = std::stoul(args[0]);
 | 
			
		||||
        if(player->sessions.count(session_id)) {
 | 
			
		||||
          player->sessions.at(session_id)->write_file(args[1], args[2]);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case net_protocol::Opcode::C2S_READ_FILE:
 | 
			
		||||
    if(!args.empty()) {
 | 
			
		||||
      fprintf(stderr, "[Player %u] Read file: '%s'\n", player->id, args[0].c_str());
 | 
			
		||||
      std::string content = player->cmd_processor->read_file(args[0]);
 | 
			
		||||
      /* Send the content back to the client. */
 | 
			
		||||
      connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_FILE_DATA,
 | 
			
		||||
                       {args[0], content}));
 | 
			
		||||
    }
 | 
			
		||||
      if(args.size() == 2) {
 | 
			
		||||
        uint32_t session_id = std::stoul(args[0]);
 | 
			
		||||
        if(player->sessions.count(session_id)) {
 | 
			
		||||
          std::string content = player->sessions.at(session_id)->read_file(args[1]);
 | 
			
		||||
          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_FILE_DATA,
 | 
			
		||||
                                                       {args[0], args[1], content}));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    case net_protocol::Opcode::C2S_COMMAND:
 | 
			
		||||
      if(!args.empty()) {
 | 
			
		||||
        fprintf(stderr, "[Player %u] Command: '%s'\n", player->id, args[0].c_str());
 | 
			
		||||
        std::string response = player->cmd_processor->process_command(args[0]);
 | 
			
		||||
 | 
			
		||||
        if(response == "__CLOSE_CONNECTION__") {
 | 
			
		||||
          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CLOSE_WINDOW));
 | 
			
		||||
          return;
 | 
			
		||||
      if(args.size() == 2) {
 | 
			
		||||
        uint32_t session_id = std::stoul(args[0]);
 | 
			
		||||
        if(player->sessions.count(session_id)) {
 | 
			
		||||
          Session* session = player->sessions.at(session_id).get();
 | 
			
		||||
          std::string response = session->process_command(args[1]);
 | 
			
		||||
          std::string new_prompt = get_full_path(session->get_current_dir());
 | 
			
		||||
          response += "\n" + new_prompt;
 | 
			
		||||
          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
 | 
			
		||||
                                                       {args[0], response}));
 | 
			
		||||
        }
 | 
			
		||||
        std::string new_prompt = get_full_path(player->cmd_processor->get_current_dir());
 | 
			
		||||
        response += "\n" + new_prompt;
 | 
			
		||||
 | 
			
		||||
        connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
 | 
			
		||||
                                                     {response}));
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
@ -202,15 +221,14 @@ void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connectio
 | 
			
		||||
  uint32_t player_id = connection->get_id();
 | 
			
		||||
  Player* player = _players[player_id].get();
 | 
			
		||||
 | 
			
		||||
  if(player && player->cmd_processor) {
 | 
			
		||||
    Machine* home_machine = player->cmd_processor->get_home_machine();
 | 
			
		||||
    Machine* session_machine = player->cmd_processor->get_session_machine();
 | 
			
		||||
 | 
			
		||||
    if(home_machine) {
 | 
			
		||||
      release_machine(home_machine->id);
 | 
			
		||||
    }
 | 
			
		||||
    if(session_machine && session_machine != home_machine) {
 | 
			
		||||
      release_machine(session_machine->id);
 | 
			
		||||
  if(player) {
 | 
			
		||||
    for(auto const& [session_id, session] : player->sessions) {
 | 
			
		||||
      Machine* home_machine = session->get_home_machine();
 | 
			
		||||
      Machine* session_machine = session->get_session_machine();
 | 
			
		||||
      if(home_machine) { release_machine(home_machine->id); }
 | 
			
		||||
      if(session_machine && session_machine != home_machine) {
 | 
			
		||||
        release_machine(session_machine->id);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,8 @@ private:
 | 
			
		||||
  std::deque<std::shared_ptr<net::TcpConnection>> _connections;
 | 
			
		||||
  std::unordered_map<uint32_t, std::unique_ptr<Player>> _players;
 | 
			
		||||
  std::map<long long, CachedMachine> _active_machines;
 | 
			
		||||
  uint32_t _next_player_id = 1;
 | 
			
		||||
  uint32_t _next_player_id  = 1;
 | 
			
		||||
  uint32_t _next_session_id = 1;
 | 
			
		||||
 | 
			
		||||
  std::unique_ptr<DatabaseManager> _db_manager;
 | 
			
		||||
  MachineManager _machine_manager;
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
#include "player.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "session.h"
 | 
			
		||||
#include "db/database_manager.h"
 | 
			
		||||
#include "i_network_bridge.h"
 | 
			
		||||
#include "machine.h"
 | 
			
		||||
@ -9,12 +9,6 @@ Player::Player(uint32_t new_id, Machine* home_machine,
 | 
			
		||||
               MachineManager* machine_manager,
 | 
			
		||||
               INetworkBridge* network_bridge) :
 | 
			
		||||
    id(new_id),
 | 
			
		||||
    state(PlayerState::AUTHENTICATING) {
 | 
			
		||||
    state(PlayerState::AUTHENTICATING) {}
 | 
			
		||||
 | 
			
		||||
  cmd_processor = new CommandProcessor(home_machine, db_manager, machine_manager,
 | 
			
		||||
                                       network_bridge);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Player::~Player(void) {
 | 
			
		||||
  delete cmd_processor;
 | 
			
		||||
}
 | 
			
		||||
Player::~Player(void) {}
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
 | 
			
		||||
#include "db/database_manager.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "session.h"
 | 
			
		||||
#include "i_network_bridge.h"
 | 
			
		||||
#include "machine.h"
 | 
			
		||||
#include "machine_manager.h"
 | 
			
		||||
@ -21,5 +23,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  uint32_t id;
 | 
			
		||||
  PlayerState state;
 | 
			
		||||
  CommandProcessor* cmd_processor; /* Manages the VFS state for the remote session. */
 | 
			
		||||
 | 
			
		||||
  /* Map of session IDs to their respective session objects. */
 | 
			
		||||
  std::unordered_map<uint32_t, std::unique_ptr<Session>> sessions;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user