[Refactor] Implment structured network protocol.
This commit is contained in:
		
							parent
							
								
									3234ef7b3b
								
							
						
					
					
						commit
						9d770ef9b2
					
				@ -6,6 +6,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "gfx/types.h"
 | 
					#include "gfx/types.h"
 | 
				
			||||||
#include "net/constants.h"
 | 
					#include "net/constants.h"
 | 
				
			||||||
 | 
					#include "net/message_protocol.h"
 | 
				
			||||||
 | 
					#include "client_network.h"
 | 
				
			||||||
#include "network_manager.h"
 | 
					#include "network_manager.h"
 | 
				
			||||||
#include "game_state.h"
 | 
					#include "game_state.h"
 | 
				
			||||||
#include "terminal.h"
 | 
					#include "terminal.h"
 | 
				
			||||||
@ -20,9 +22,9 @@
 | 
				
			|||||||
#include "debug/debug_overlay.h"
 | 
					#include "debug/debug_overlay.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GameState::_init_desktop(void) {
 | 
					void GameState::_init_desktop(void) {
 | 
				
			||||||
  _desktop = std::make_unique<Desktop>(_screen_width, _screen_height, _network.get());
 | 
					  _desktop = std::make_unique<Desktop>(_screen_width, _screen_height, this); 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  auto term = std::make_unique<Terminal>(_network.get());
 | 
					  auto term = std::make_unique<Terminal>(this);
 | 
				
			||||||
  auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
 | 
					  auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
 | 
				
			||||||
  term_window->set_content(std::move(term));
 | 
					  term_window->set_content(std::move(term));
 | 
				
			||||||
  _desktop->add_window(std::move(term_window));
 | 
					  _desktop->add_window(std::move(term_window));
 | 
				
			||||||
@ -149,11 +151,11 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      if(_login_screen->is_new_account_mode()) {
 | 
					      if(_login_screen->is_new_account_mode()) {
 | 
				
			||||||
        std::string hostname = _login_screen->get_hostname();
 | 
					        std::string hostname = _login_screen->get_hostname();
 | 
				
			||||||
        std::string msg = "C_ACC::" + username + "::" + password + "::" + hostname;
 | 
					        _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_CREATE_ACCOUNT,
 | 
				
			||||||
        _network->send(msg);
 | 
					                                                   {username, password, hostname}));
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        std::string msg = "LOGIN::" + username + "::" + password;
 | 
					        _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_LOGIN,
 | 
				
			||||||
        _network->send(msg);
 | 
					                                                   {username, password}));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      _login_screen->clear_login_attempt(); /* Try to spam my server now b.tch! */
 | 
					      _login_screen->clear_login_attempt(); /* Try to spam my server now b.tch! */
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -161,18 +163,36 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
 | 
				
			|||||||
    /* Check for server response to our login attempt. */
 | 
					    /* Check for server response to our login attempt. */
 | 
				
			||||||
    std::string server_msg;
 | 
					    std::string server_msg;
 | 
				
			||||||
    while(_network->poll_message(server_msg)) {
 | 
					    while(_network->poll_message(server_msg)) {
 | 
				
			||||||
      if(server_msg == "LOGIN_SUCCESS" || server_msg == "C_ACC_SUCCESS") {
 | 
					      net_protocol::Opcode opcode;
 | 
				
			||||||
        _current_screen = Screen::BOOTING;
 | 
					      std::vector<std::string> args;
 | 
				
			||||||
        _login_screen.reset(); /* Free mem. */
 | 
					      net_protocol::parse_message(server_msg, opcode, args);
 | 
				
			||||||
        _boot_sequence = std::make_unique<BootSequence>();
 | 
					
 | 
				
			||||||
 | 
					      switch(opcode) {
 | 
				
			||||||
 | 
					        case net_protocol::Opcode::S2C_LOGIN_SUCCESS:
 | 
				
			||||||
 | 
					        case net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS:
 | 
				
			||||||
 | 
					          _current_screen = Screen::BOOTING;
 | 
				
			||||||
 | 
					          _login_screen.reset(); /* Free mem. */
 | 
				
			||||||
 | 
					          _boot_sequence = std::make_unique<BootSequence>();
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case net_protocol::Opcode::S2C_LOGIN_FAIL:
 | 
				
			||||||
 | 
					          _login_screen->set_error_message(args.empty() ? "Login failed." : args[0]);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        case net_protocol::Opcode::S2C_CREATE_ACCOUNT_FAIL:
 | 
				
			||||||
 | 
					          _login_screen->set_error_message(args.empty() ? "Account creation failed." :
 | 
				
			||||||
 | 
					                                            args[0]);
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					        default:
 | 
				
			||||||
 | 
					          fprintf(stderr, "Recieved unexpected opcode %d during login.\n",
 | 
				
			||||||
 | 
					                  static_cast<int>(opcode));
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      /* If we successfully changed screen, stop processing messages for this frame. */
 | 
				
			||||||
 | 
					      if(_current_screen == Screen::BOOTING) {
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
      } else if(server_msg == "LOGIN_FAIL") {
 | 
					 | 
				
			||||||
        _login_screen->set_error_message("Invalid username or password.");
 | 
					 | 
				
			||||||
      } else if(server_msg == "C_ACC_FAIL") {
 | 
					 | 
				
			||||||
        _login_screen->set_error_message("Username already exists.");
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } break;
 | 
					    break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  case Screen::BOOTING: {
 | 
					  case Screen::BOOTING: {
 | 
				
			||||||
    if(!_boot_sequence) break; /* Shouldn't happen. */
 | 
					    if(!_boot_sequence) break; /* Shouldn't happen. */
 | 
				
			||||||
    if(_boot_sequence->is_finished()) {
 | 
					    if(_boot_sequence->is_finished()) {
 | 
				
			||||||
@ -184,68 +204,81 @@ void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  case Screen::DESKTOP: {
 | 
					  case Screen::DESKTOP: {
 | 
				
			||||||
    std::string server_msg;
 | 
					    std::string server_msg;
 | 
				
			||||||
      while(_network->poll_message(server_msg)) {
 | 
					    while(_network->poll_message(server_msg)) {
 | 
				
			||||||
        /* Check for 'special', non-terminal messages first. */
 | 
					      net_protocol::Opcode opcode;
 | 
				
			||||||
          if(server_msg.rfind("FILEDATA::", 0) == 0) {
 | 
					      std::vector<std::string> args;
 | 
				
			||||||
            std::string payload = server_msg.substr(10);
 | 
					      net_protocol::parse_message(server_msg, opcode, args);
 | 
				
			||||||
            size_t separator_pos = payload.find("::");
 | 
					
 | 
				
			||||||
            if(separator_pos != std::string::npos) {
 | 
					      switch(opcode) {
 | 
				
			||||||
              std::string filepath = payload.substr(0, separator_pos);
 | 
					        case net_protocol::Opcode::S2C_FILE_DATA:
 | 
				
			||||||
              std::string content = payload.substr(separator_pos+2);
 | 
					          if(args.size() == 2) {
 | 
				
			||||||
              auto editor = std::make_unique<Editor>(filepath);
 | 
					            auto editor = std::make_unique<Editor>(args[0]);
 | 
				
			||||||
              editor->set_buffer_content(content);
 | 
					            editor->set_buffer_content(args[1]);
 | 
				
			||||||
              auto editor_window = std::make_unique<UIWindow>(filepath.c_str(),
 | 
					            auto editor_window = std::make_unique<UIWindow>(args[0].c_str(),
 | 
				
			||||||
                                                              200, 200, 600, 400);
 | 
					                                                            200, 200, 600, 400);
 | 
				
			||||||
              editor_window->set_content(std::move(editor));
 | 
					            editor_window->set_content(std::move(editor));
 | 
				
			||||||
              _desktop->add_window(std::move(editor_window));
 | 
					            _desktop->add_window(std::move(editor_window));
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          continue;
 | 
					          break;
 | 
				
			||||||
        }
 | 
					        case net_protocol::Opcode::S2C_DISCONNECT: {
 | 
				
			||||||
        if(server_msg == "__DISCONNECTED__") {
 | 
					 | 
				
			||||||
          IWindowContent* content = _desktop->get_focused_window() ?
 | 
					          IWindowContent* content = _desktop->get_focused_window() ?
 | 
				
			||||||
                _desktop->get_focused_window()->get_content() : nullptr;
 | 
					              _desktop->get_focused_window()->get_content() : nullptr;
 | 
				
			||||||
          Terminal* terminal = dynamic_cast<Terminal*>(content);
 | 
					          Terminal* terminal = dynamic_cast<Terminal*>(content);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          if(terminal) {
 | 
					          if(terminal) {
 | 
				
			||||||
            terminal->add_history("Connection closed.");
 | 
					            terminal->add_history("Connection closed.");
 | 
				
			||||||
            _network->send("");
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          continue;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        /* If not 'special' message, assume it's for terminal. */
 | 
					        break;
 | 
				
			||||||
        IWindowContent* content = _desktop->get_focused_window() ?
 | 
					        case net_protocol::Opcode::S2C_CLOSE_WINDOW: {
 | 
				
			||||||
            _desktop->get_focused_window()->get_content() : nullptr;
 | 
					          if(_desktop) {
 | 
				
			||||||
        Terminal* terminal = dynamic_cast<Terminal*>(content);
 | 
					            UIWindow* focused_window = _desktop->get_focused_window();
 | 
				
			||||||
        if(!terminal)
 | 
					            if(focused_window) { focused_window->close(); }
 | 
				
			||||||
          continue;
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Server sends "output\nprompt", split them. */
 | 
					            /* Server sends "output\nprompt", split them. */
 | 
				
			||||||
        size_t last_newline = server_msg.find_last_of('\n');
 | 
					            size_t last_newline = args[0].find_last_of('\n');
 | 
				
			||||||
        if(last_newline != std::string::npos) {
 | 
					            if(last_newline != std::string::npos) {
 | 
				
			||||||
          std::string prompt = server_msg.substr(last_newline+1);
 | 
					              std::string prompt = args[0].substr(last_newline+1);
 | 
				
			||||||
          terminal->set_prompt(prompt);
 | 
					              terminal->set_prompt(prompt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          std::string output = server_msg.substr(0, last_newline);
 | 
					              std::string output = args[0].substr(0, last_newline);
 | 
				
			||||||
          if(!output.empty()) {
 | 
					              if(!output.empty()) {
 | 
				
			||||||
            /* Split multiline output and push each line to history. */
 | 
					                /* Split multiline output and push each line to history. */
 | 
				
			||||||
            std::stringstream ss(output);
 | 
					                std::stringstream ss(output);
 | 
				
			||||||
            std::string line;
 | 
					                std::string line;
 | 
				
			||||||
            while(std::getline(ss, line, '\n')) {
 | 
					                while(std::getline(ss, line, '\n')) {
 | 
				
			||||||
              terminal->add_history(line);
 | 
					                  terminal->add_history(line);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              terminal->add_history(args[0]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          terminal->add_history(server_msg);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if(_desktop) {
 | 
					 | 
				
			||||||
        /*
 | 
					 | 
				
			||||||
         * TODO: These fuck'in window dimensions just need to be global at this point!!
 | 
					 | 
				
			||||||
         * Pass GameState by reference ?
 | 
					 | 
				
			||||||
         */
 | 
					 | 
				
			||||||
        _desktop->update(dt, _screen_width, _screen_height);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      break;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    if(_desktop) {
 | 
				
			||||||
 | 
					      _desktop->update(dt, _screen_width, _screen_height);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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}));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -277,3 +310,13 @@ void GameState::render(const RenderContext& context) {
 | 
				
			|||||||
    _debug_overlay->render(context.ui_renderer);
 | 
					    _debug_overlay->render(context.ui_renderer);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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_file_read_request(const std::string& path) {
 | 
				
			||||||
 | 
					  _network->send(net_protocol::build_message(net_protocol::Opcode::C2S_READ_FILE,
 | 
				
			||||||
 | 
					                                             {path}));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -32,6 +32,11 @@ public:
 | 
				
			|||||||
  void update(float dt, int draw_calls, int shape_verts, int text_verts);
 | 
					  void update(float dt, int draw_calls, int shape_verts, int text_verts);
 | 
				
			||||||
  void render(const RenderContext& context);
 | 
					  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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
  std::unique_ptr<ClientNetwork>  _network;
 | 
					  std::unique_ptr<ClientNetwork>  _network;
 | 
				
			||||||
  std::unique_ptr<Desktop>        _desktop;
 | 
					  std::unique_ptr<Desktop>        _desktop;
 | 
				
			||||||
@ -46,5 +51,6 @@ private:
 | 
				
			|||||||
  bool                            _is_single_player;
 | 
					  bool                            _is_single_player;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _init_desktop(void);
 | 
					  void _init_desktop(void);
 | 
				
			||||||
 | 
					  void _send_network_command(const std::string& command);
 | 
				
			||||||
  void _run_server(void);
 | 
					  void _run_server(void);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -6,12 +6,12 @@
 | 
				
			|||||||
#include <SDL3/SDL_events.h>
 | 
					#include <SDL3/SDL_events.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "terminal.h"
 | 
					#include "terminal.h"
 | 
				
			||||||
#include "client_network.h"
 | 
					#include "game_state.h"
 | 
				
			||||||
#include "gfx/types.h"
 | 
					#include "gfx/types.h"
 | 
				
			||||||
#include "ui/window_action.h"
 | 
					#include "ui/window_action.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Terminal::Terminal(ClientNetwork* network)
 | 
					Terminal::Terminal(GameState* game_state)
 | 
				
			||||||
    : _network(network), _should_close(false), _scroll_offset(0),
 | 
					    : _game_state(game_state), _should_close(false), _scroll_offset(0),
 | 
				
			||||||
      _prompt(""), _pending_action({ActionType::NONE}) {
 | 
					      _prompt(""), _pending_action({ActionType::NONE}) {
 | 
				
			||||||
  _input_view = std::make_unique<TextView>(&_input_buffer, false);
 | 
					  _input_view = std::make_unique<TextView>(&_input_buffer, false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -74,7 +74,7 @@ void Terminal::_on_ret_press(void) {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  _history.push_back(_prompt + "> " + command);
 | 
					  _history.push_back(_prompt + "> " + command);
 | 
				
			||||||
  _network->send(command);
 | 
					  _game_state->send_network_command(command);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Terminal::handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) {
 | 
					void Terminal::handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) {
 | 
				
			||||||
 | 
				
			|||||||
@ -4,16 +4,17 @@
 | 
				
			|||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <SDL3/SDL.h>
 | 
					#include <SDL3/SDL.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "client_network.h"
 | 
					 | 
				
			||||||
#include "gfx/types.h"
 | 
					#include "gfx/types.h"
 | 
				
			||||||
#include "ui/text_buffer.h"
 | 
					#include "ui/text_buffer.h"
 | 
				
			||||||
#include "ui/text_view.h"
 | 
					#include "ui/text_view.h"
 | 
				
			||||||
#include "ui/i_window_content.h"
 | 
					#include "ui/i_window_content.h"
 | 
				
			||||||
#include "ui/window_action.h"
 | 
					#include "ui/window_action.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GameState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Terminal : public IWindowContent {
 | 
					class Terminal : public IWindowContent {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
  Terminal(ClientNetwork* network);
 | 
					  Terminal(GameState* game_state);
 | 
				
			||||||
  ~Terminal(void);
 | 
					  ~Terminal(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void update(void) override;
 | 
					  void update(void) override;
 | 
				
			||||||
@ -33,7 +34,7 @@ private:
 | 
				
			|||||||
  std::vector<std::string> _history;
 | 
					  std::vector<std::string> _history;
 | 
				
			||||||
  int _scroll_offset;
 | 
					  int _scroll_offset;
 | 
				
			||||||
  std::string _prompt;
 | 
					  std::string _prompt;
 | 
				
			||||||
  ClientNetwork* _network;
 | 
					  GameState* _game_state;
 | 
				
			||||||
  TextBuffer _input_buffer;
 | 
					  TextBuffer _input_buffer;
 | 
				
			||||||
  WindowAction _pending_action;
 | 
					  WindowAction _pending_action;
 | 
				
			||||||
  std::unique_ptr<TextView> _input_view;
 | 
					  std::unique_ptr<TextView> _input_view;
 | 
				
			||||||
 | 
				
			|||||||
@ -8,14 +8,14 @@
 | 
				
			|||||||
#include <SDL3/SDL_video.h>
 | 
					#include <SDL3/SDL_video.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "gfx/types.h"
 | 
					#include "gfx/types.h"
 | 
				
			||||||
#include "ui/editor.h"
 | 
					#include "game_state.h"
 | 
				
			||||||
#include "desktop.h"
 | 
					#include "desktop.h"
 | 
				
			||||||
#include "client_network.h"
 | 
					 | 
				
			||||||
#include "terminal.h"
 | 
					#include "terminal.h"
 | 
				
			||||||
#include "ui/i_window_content.h"
 | 
					#include "ui/i_window_content.h"
 | 
				
			||||||
#include "ui/launcher.h"
 | 
					#include "ui/launcher.h"
 | 
				
			||||||
#include "ui/taskbar.h"
 | 
					#include "ui/taskbar.h"
 | 
				
			||||||
#include "ui/ui_window.h"
 | 
					#include "ui/ui_window.h"
 | 
				
			||||||
 | 
					#include "ui/editor.h"
 | 
				
			||||||
#include "ui/window_action.h"
 | 
					#include "ui/window_action.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const std::string& get_random_snippet(const std::vector<std::string>& snippets) {
 | 
					static const std::string& get_random_snippet(const std::vector<std::string>& snippets) {
 | 
				
			||||||
@ -26,9 +26,9 @@ static const std::string& get_random_snippet(const std::vector<std::string>& sni
 | 
				
			|||||||
  return snippets[rand() % snippets.size()];
 | 
					  return snippets[rand() % snippets.size()];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Desktop::Desktop(int screen_width, int screen_height, ClientNetwork* network) {
 | 
					Desktop::Desktop(int screen_width, int screen_height, GameState* game_state) {
 | 
				
			||||||
  _taskbar = std::make_unique<Taskbar>(screen_width, screen_height);
 | 
					  _taskbar = std::make_unique<Taskbar>(screen_width, screen_height);
 | 
				
			||||||
  _network = network;
 | 
					  _game_state= game_state;
 | 
				
			||||||
  _focused_window = nullptr;
 | 
					  _focused_window = nullptr;
 | 
				
			||||||
  _launcher_is_open = false;
 | 
					  _launcher_is_open = false;
 | 
				
			||||||
  _launcher = std::make_unique<Launcher>(5, 0, 200); /* Tmp y-coord. */
 | 
					  _launcher = std::make_unique<Launcher>(5, 0, 200); /* Tmp y-coord. */
 | 
				
			||||||
@ -133,7 +133,7 @@ void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height
 | 
				
			|||||||
    } else if(_launcher_is_open) {
 | 
					    } else if(_launcher_is_open) {
 | 
				
			||||||
      std::string app_to_launch = _launcher->handle_event(event, screen_height);
 | 
					      std::string app_to_launch = _launcher->handle_event(event, screen_height);
 | 
				
			||||||
      if(app_to_launch == "Terminal") {
 | 
					      if(app_to_launch == "Terminal") {
 | 
				
			||||||
        auto term = std::make_unique<Terminal>(_network);
 | 
					        auto term = std::make_unique<Terminal>(_game_state);
 | 
				
			||||||
        auto term_window = std::make_unique<UIWindow>("Terminal", 150, 150, 800, 500);
 | 
					        auto term_window = std::make_unique<UIWindow>("Terminal", 150, 150, 800, 500);
 | 
				
			||||||
        term_window->set_content(std::move(term));
 | 
					        term_window->set_content(std::move(term));
 | 
				
			||||||
        add_window(std::move(term_window));
 | 
					        add_window(std::move(term_window));
 | 
				
			||||||
@ -182,13 +182,11 @@ void Desktop::update(float dt, int screen_width, int screen_height) {
 | 
				
			|||||||
      WindowAction action = content->get_pending_action();
 | 
					      WindowAction action = content->get_pending_action();
 | 
				
			||||||
      switch(action.type) {
 | 
					      switch(action.type) {
 | 
				
			||||||
        case ActionType::WRITE_FILE: {
 | 
					        case ActionType::WRITE_FILE: {
 | 
				
			||||||
          std::string message = "WRITEF::" + action.payload1 + "::" + action.payload2;
 | 
					          _game_state->send_file_write_request(action.payload1, action.payload2);
 | 
				
			||||||
          _network->send(message);
 | 
					 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        case ActionType::READ_FILE: {
 | 
					        case ActionType::READ_FILE: {
 | 
				
			||||||
          std::string message = "READF::" + action.payload1;
 | 
					          _game_state->send_file_read_request(action.payload1);
 | 
				
			||||||
          _network->send(message);
 | 
					 | 
				
			||||||
          break;
 | 
					          break;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
 | 
				
			|||||||
@ -4,13 +4,14 @@
 | 
				
			|||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
#include <SDL3/SDL.h>
 | 
					#include <SDL3/SDL.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "client_network.h"
 | 
					 | 
				
			||||||
#include "gfx/types.h"
 | 
					#include "gfx/types.h"
 | 
				
			||||||
#include "ui/ui_window.h"
 | 
					#include "ui/ui_window.h"
 | 
				
			||||||
#include "ui/taskbar.h"
 | 
					#include "ui/taskbar.h"
 | 
				
			||||||
#include "ui/launcher.h"
 | 
					#include "ui/launcher.h"
 | 
				
			||||||
#include "ui/ui_renderer.h"
 | 
					#include "ui/ui_renderer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GameState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Animated background stuff. */
 | 
					/* Animated background stuff. */
 | 
				
			||||||
struct ScrollingText {
 | 
					struct ScrollingText {
 | 
				
			||||||
  std::string text;
 | 
					  std::string text;
 | 
				
			||||||
@ -22,7 +23,7 @@ struct ScrollingText {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Desktop {
 | 
					class Desktop {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
  Desktop(int screen_width, int screen_height, ClientNetwork* network);
 | 
					  Desktop(int screen_width, int screen_height, GameState* game_state);
 | 
				
			||||||
  ~Desktop(void);
 | 
					  ~Desktop(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void add_window(std::unique_ptr<UIWindow> window);
 | 
					  void add_window(std::unique_ptr<UIWindow> window);
 | 
				
			||||||
@ -41,7 +42,7 @@ private:
 | 
				
			|||||||
  std::unique_ptr<Taskbar>   _taskbar;
 | 
					  std::unique_ptr<Taskbar>   _taskbar;
 | 
				
			||||||
  std::unique_ptr<Launcher>  _launcher;
 | 
					  std::unique_ptr<Launcher>  _launcher;
 | 
				
			||||||
  UIWindow*                  _focused_window;
 | 
					  UIWindow*                  _focused_window;
 | 
				
			||||||
  ClientNetwork*             _network;
 | 
					  GameState*                 _game_state;
 | 
				
			||||||
  std::vector<ScrollingText> _background_text;
 | 
					  std::vector<ScrollingText> _background_text;
 | 
				
			||||||
  std::vector<std::string>   _snippets;
 | 
					  std::vector<std::string>   _snippets;
 | 
				
			||||||
  bool                       _launcher_is_open;
 | 
					  bool                       _launcher_is_open;
 | 
				
			||||||
 | 
				
			|||||||
@ -35,6 +35,7 @@ public:
 | 
				
			|||||||
                    int taskbar_height);
 | 
					                    int taskbar_height);
 | 
				
			||||||
  void minimize(void);
 | 
					  void minimize(void);
 | 
				
			||||||
  void restore(void);
 | 
					  void restore(void);
 | 
				
			||||||
 | 
					  void close(void) { _should_close = true; }
 | 
				
			||||||
  bool is_minimized(void) const;
 | 
					  bool is_minimized(void) const;
 | 
				
			||||||
  bool should_close(void) const;
 | 
					  bool should_close(void) const;
 | 
				
			||||||
  void set_focused(bool focused);
 | 
					  void set_focused(bool focused);
 | 
				
			||||||
 | 
				
			|||||||
@ -97,7 +97,10 @@ std::string CommandProcessor::process_command(const std::string& command) {
 | 
				
			|||||||
                      "ON T1.id = T2.parent_id WHERE T1.name = 'bin' AND T2.name = ? "
 | 
					                      "ON T1.id = T2.parent_id WHERE T1.name = 'bin' AND T2.name = ? "
 | 
				
			||||||
                      "AND t1.machine_id = ?;"
 | 
					                      "AND t1.machine_id = ?;"
 | 
				
			||||||
                   << script_filename << _session_machine->id
 | 
					                   << script_filename << _session_machine->id
 | 
				
			||||||
                   >> std::tie(script_id, script_content);
 | 
					                   >> [&](long long id, std::string content) {
 | 
				
			||||||
 | 
					                        script_id = id;
 | 
				
			||||||
 | 
					                        script_content = content;
 | 
				
			||||||
 | 
					                      };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if(script_id > 0) {
 | 
					  if(script_id > 0) {
 | 
				
			||||||
    bool is_remote = (_session_machine != _home_machine);
 | 
					    bool is_remote = (_session_machine != _home_machine);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										37
									
								
								common/src/net/message_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								common/src/net/message_protocol.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include "message_protocol.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace net_protocol {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string build_message(Opcode opcode, const std::vector<std::string>& args) {
 | 
				
			||||||
 | 
					  std::string payload;
 | 
				
			||||||
 | 
					  payload += static_cast<char>(opcode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for(size_t i = 0; i < args.size(); ++i) {
 | 
				
			||||||
 | 
					    payload.append(args[i]);
 | 
				
			||||||
 | 
					    if(i < args.size() - 1) {
 | 
				
			||||||
 | 
					      payload += '\0';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return payload;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void parse_message(const std::string& payload, Opcode& out_opcode,
 | 
				
			||||||
 | 
					                   std::vector<std::string>& out_args) {
 | 
				
			||||||
 | 
					  out_args.clear();
 | 
				
			||||||
 | 
					  if(payload.empty()) {
 | 
				
			||||||
 | 
					    return; /* Might handle this as an error.. */
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  out_opcode = static_cast<Opcode>(payload[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if(payload.length() > 1) {
 | 
				
			||||||
 | 
					    std::stringstream ss(payload.substr(1));
 | 
				
			||||||
 | 
					    std::string segment;
 | 
				
			||||||
 | 
					    while(std::getline(ss, segment, '\0')) {
 | 
				
			||||||
 | 
					      out_args.push_back(segment);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; /* namespace net_protocol. */
 | 
				
			||||||
							
								
								
									
										53
									
								
								common/src/net/message_protocol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								common/src/net/message_protocol.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <cstdint>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace net_protocol {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Defines the opcodes for client-server communication.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Each message payload begins with one of these single-byte opcodes to identify
 | 
				
			||||||
 | 
					 * the purpose of the message.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					enum class Opcode : uint8_t {
 | 
				
			||||||
 | 
					  /* Client -> Server messages. */
 | 
				
			||||||
 | 
					  C2S_CREATE_ACCOUNT,
 | 
				
			||||||
 | 
					  C2S_LOGIN,
 | 
				
			||||||
 | 
					  C2S_COMMAND,
 | 
				
			||||||
 | 
					  C2S_WRITE_FILE,
 | 
				
			||||||
 | 
					  C2S_READ_FILE,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* 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,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Builds a raw message payload from an opcode and arguments.
 | 
				
			||||||
 | 
					 * @param opcode The message type.
 | 
				
			||||||
 | 
					 * @param args Vector of string arguments to be included in the message.
 | 
				
			||||||
 | 
					 * @return A payload string to be sent over the network.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					std::string build_message(Opcode opcode, const std::vector<std::string>& args = {});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @brief Parse a raw message payload into its opcode and arguments.
 | 
				
			||||||
 | 
					 * @param payload The raw payload received from the netwrok.
 | 
				
			||||||
 | 
					 * @param out_opcode The output variable for the parsed opcode.
 | 
				
			||||||
 | 
					 * @param out_args the output vector for the parsed string arguments.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The format is: [1-byte Opcode][arg1]\0[arg2]\0...
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void parse_message(const std::string& payload, Opcode& out_opcode,
 | 
				
			||||||
 | 
					                   std::vector<std::string>& out_args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; /* namespace net_protocol. */
 | 
				
			||||||
@ -4,6 +4,7 @@
 | 
				
			|||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "network_manager.h"
 | 
					#include "network_manager.h"
 | 
				
			||||||
 | 
					#include "net/message_protocol.h"
 | 
				
			||||||
#include "db/database_manager.h"
 | 
					#include "db/database_manager.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "asio/error_code.hpp"
 | 
					#include "asio/error_code.hpp"
 | 
				
			||||||
@ -95,10 +96,6 @@ void NetworkManager::start_accept(void) {
 | 
				
			|||||||
            },
 | 
					            },
 | 
				
			||||||
            [this, new_connection]() { this->on_disconnect(new_connection); });
 | 
					            [this, new_connection]() { this->on_disconnect(new_connection); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* Send initial prompt. */
 | 
					 | 
				
			||||||
        std::string prompt = "\n" + get_full_path(new_player_ptr->cmd_processor->get_current_dir());
 | 
					 | 
				
			||||||
        new_connection->send(prompt);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        _connections.push_back(new_connection);
 | 
					        _connections.push_back(new_connection);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        fprintf(stderr, "Accept error: %s\n", ec.message().c_str());
 | 
					        fprintf(stderr, "Accept error: %s\n", ec.message().c_str());
 | 
				
			||||||
@ -108,17 +105,6 @@ void NetworkManager::start_accept(void) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static std::vector<std::string> split_message(const std::string& s, const std::string& delimiter) {
 | 
					 | 
				
			||||||
  std::vector<std::string> tokens;
 | 
					 | 
				
			||||||
  size_t start = 0, end = 0;
 | 
					 | 
				
			||||||
  while((end = s.find(delimiter, start)) != std::string::npos) {
 | 
					 | 
				
			||||||
    tokens.push_back(s.substr(start, end-start));
 | 
					 | 
				
			||||||
    start = end + delimiter.length();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  tokens.push_back(s.substr(start));
 | 
					 | 
				
			||||||
  return tokens;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
 | 
					void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
 | 
				
			||||||
                                const std::string& message) {
 | 
					                                const std::string& message) {
 | 
				
			||||||
  Player* player = _players[connection->get_id()].get();
 | 
					  Player* player = _players[connection->get_id()].get();
 | 
				
			||||||
@ -127,94 +113,103 @@ void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
 | 
				
			|||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  net_protocol::Opcode opcode;
 | 
				
			||||||
 | 
					  std::vector<std::string> args;
 | 
				
			||||||
 | 
					    net_protocol::parse_message(message, opcode, args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if(player->state == PlayerState::AUTHENTICATING) {
 | 
					  if(player->state == PlayerState::AUTHENTICATING) {
 | 
				
			||||||
    if(message.rfind("C_ACC::", 0) == 0) {
 | 
					    switch(opcode) {
 | 
				
			||||||
      std::string payload = message.substr(7);
 | 
					      case net_protocol::Opcode::C2S_CREATE_ACCOUNT:
 | 
				
			||||||
      auto parts = split_message(payload, "::");
 | 
					        if(args.size() == 3) {
 | 
				
			||||||
      if(parts.size() == 3) {
 | 
					          if(_db_manager->create_player(args[0], args[1], args[2],
 | 
				
			||||||
        if(_db_manager->create_player(parts[0], parts[1], parts[2],
 | 
					 | 
				
			||||||
                                      _machine_manager.get_vfs_template())) {
 | 
					                                      _machine_manager.get_vfs_template())) {
 | 
				
			||||||
          long long machine_id = _db_manager->players().get_home_machine_id(parts[0]);
 | 
					          long long machine_id = _db_manager->players().get_home_machine_id(args[0]);
 | 
				
			||||||
          Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
 | 
					          Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
 | 
				
			||||||
          delete player->cmd_processor; /* Delete old command processor. */
 | 
					          delete player->cmd_processor; /* Delete old command processor. */
 | 
				
			||||||
          player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
 | 
					          player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
 | 
				
			||||||
                                                       _db_manager.get(),
 | 
					                                                       _db_manager.get(),
 | 
				
			||||||
                                                       &_machine_manager);
 | 
					                                                       &_machine_manager);
 | 
				
			||||||
          player->state = PlayerState::ACTIVE;
 | 
					          player->state = PlayerState::ACTIVE;
 | 
				
			||||||
          connection->send("C_ACC_SUCCESS");
 | 
					          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS));
 | 
				
			||||||
          /* send initial prompt. */
 | 
					          /* send initial prompt. */
 | 
				
			||||||
          std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
 | 
					          std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
 | 
				
			||||||
          connection->send(prompt);
 | 
					          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
 | 
				
			||||||
 | 
					                                                       {prompt}));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          connection->send("C_ACC_FAIL");
 | 
					          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_FAIL,
 | 
				
			||||||
 | 
					                                                       {"Username already exists."}));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else if(message.rfind("LOGIN::", 0) == 0) {
 | 
					      break;
 | 
				
			||||||
      std::string payload = message.substr(7);
 | 
					      case net_protocol::Opcode::C2S_LOGIN:
 | 
				
			||||||
      auto parts = split_message(payload, "::");
 | 
					        if(args.size() == 2) {
 | 
				
			||||||
      if(parts.size() == 2) {
 | 
					          if(_db_manager->players().authenticate(args[0], args[1])) {
 | 
				
			||||||
        if(_db_manager->players().authenticate(parts[0], parts[1])) {
 | 
					            long long machine_id = _db_manager->players().get_home_machine_id(args[0]);
 | 
				
			||||||
          long long machine_id = _db_manager->players().get_home_machine_id(parts[0]);
 | 
					            printf("DEBUG: Loading machine %lld for player %s\n", machine_id, args[0].c_str());
 | 
				
			||||||
          printf("DEBUG: Loading machine %lld for player %s\n", machine_id, parts[0].c_str());
 | 
					 | 
				
			||||||
          Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
 | 
					          Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
 | 
				
			||||||
          delete player->cmd_processor; /* Delete old command processor. */
 | 
					          delete player->cmd_processor; /* Delete old command processor. */
 | 
				
			||||||
          player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
 | 
					          player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
 | 
				
			||||||
                                                       _db_manager.get(), &_machine_manager);
 | 
					                                                       _db_manager.get(), &_machine_manager);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          player->state = PlayerState::ACTIVE;
 | 
					          player->state = PlayerState::ACTIVE;
 | 
				
			||||||
          connection->send("LOGIN_SUCCESS");
 | 
					            connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_LOGIN_SUCCESS));
 | 
				
			||||||
          /* Send initial prompt. */
 | 
					          /* Send initial prompt. */
 | 
				
			||||||
          std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
 | 
					          std::string prompt = "\n" + get_full_path(player->cmd_processor->get_current_dir());
 | 
				
			||||||
          connection->send(prompt);
 | 
					          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_COMMAND_RESPONSE,
 | 
				
			||||||
 | 
					                                                    {prompt}));
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          connection->send("LOGIN_FAIL");
 | 
					          connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_LOGIN_FAIL,
 | 
				
			||||||
 | 
					                                                       {"Invalid username or password."}));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					      break;
 | 
				
			||||||
      /* Ignore all other messages while authing. */
 | 
					      default:
 | 
				
			||||||
      connection->send("ERR: Not authenticated.\n");
 | 
					        /* ignore all other messages while authing. */
 | 
				
			||||||
 | 
					        fprintf(stderr, "Received invalid opcode %d during auth.\n", static_cast<int>(opcode));
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  /* === PLAYER BECOMES ACTIVE HERE === */
 | 
					  /* === PLAYER BECOMES ACTIVE HERE === */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Check for "special" message prefixes. */
 | 
					  switch(opcode) {
 | 
				
			||||||
  if(message.rfind("WRITEF::", 0) == 0) {
 | 
					    case net_protocol::Opcode::C2S_WRITE_FILE:
 | 
				
			||||||
    /* Message format: WRITEF::/path/to/file::content. */
 | 
					      if(args.size() == 2) {
 | 
				
			||||||
    std::string payload = message.substr(8);
 | 
					        fprintf(stderr, "[Player %u] Write file: '%s'\n", player->id, args[0].c_str());
 | 
				
			||||||
    size_t separator_pos = payload.find("::");
 | 
					        player->cmd_processor->write_file(args[0], args[1]);
 | 
				
			||||||
    if(separator_pos != std::string::npos) {
 | 
					 | 
				
			||||||
      std::string filepath = payload.substr(0, separator_pos);
 | 
					 | 
				
			||||||
      std::string content = payload.substr(separator_pos+2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      fprintf(stderr, "[Player %u] Write file: \'%s\'\n", player->id, filepath.c_str());
 | 
					 | 
				
			||||||
      player->cmd_processor->write_file(filepath, content);
 | 
					 | 
				
			||||||
      /* Response not required for a file write. */
 | 
					      /* Response not required for a file write. */
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return;
 | 
					    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}));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					      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;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        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:
 | 
				
			||||||
 | 
					      fprintf(stderr, "Received invalid opcode %d from active player.\n",
 | 
				
			||||||
 | 
					              static_cast<int>(opcode));
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  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());
 | 
					 | 
				
			||||||
  std::string response = player->cmd_processor->process_command(message);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if(response == "__CLOSE_CONNECTION__") {
 | 
					 | 
				
			||||||
    connection->send(response);
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::string new_prompt = get_full_path(player->cmd_processor->get_current_dir());
 | 
					 | 
				
			||||||
  response += "\n" + new_prompt;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  connection->send(response);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connection) {
 | 
					void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connection) {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user