[Add] Remote SSH sessions and window management.
This is the beginning of the remote session functionality. It allows players to connect to different NPC systems via an 'ssh' command. *Server* - Can now manage a world of multiple NPC file systems that are identified by IP addresses. - Implemented SSH command to allow connection to remote NPC systems. Each remote session is managed via a 'CommandProcessor'. - 'exit' command causes the remote server to terminate the client connection *Client* - Terminal can enter a 'remote' state via the SSH command which sends subsequent commands to the server. - the local 'exit' command will close the terminal window. - Refactored UI object ownership to prevent memory leaks and ensure proper cleanup when a window is closed or the terminal application.
This commit is contained in:
		
							parent
							
								
									0c4a98a03d
								
							
						
					
					
						commit
						208314f54a
					
				@ -60,6 +60,13 @@ void ClientNetwork::send_command(const char* command) {
 | 
			
		||||
  NET_WriteToStreamSocket(_socket, command, strlen(command)+1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ClientNetwork::disconnect(void) {
 | 
			
		||||
  if(_socket) {
 | 
			
		||||
    NET_DestroyStreamSocket(_socket);
 | 
			
		||||
    _socket = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ClientNetwork::receive_response(void) {
 | 
			
		||||
  if(!_socket) return "";
 | 
			
		||||
  char buffer[2048];
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  bool connect(const char* host, int port);
 | 
			
		||||
  void send_command(const char* command);
 | 
			
		||||
  void disconnect(void);
 | 
			
		||||
  std::string receive_response(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
@ -88,6 +88,8 @@ int main(int argc, char** argv) {
 | 
			
		||||
      desktop->handle_event(&event);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    desktop->update();
 | 
			
		||||
 | 
			
		||||
    Uint32 current_time = SDL_GetTicks();
 | 
			
		||||
    if(current_time - last_blink_time > 500) { /* Every 500ms. */
 | 
			
		||||
      show_cursor = !show_cursor;
 | 
			
		||||
@ -106,8 +108,6 @@ int main(int argc, char** argv) {
 | 
			
		||||
 | 
			
		||||
  /* Cleanup. */
 | 
			
		||||
  delete desktop;
 | 
			
		||||
  delete term;
 | 
			
		||||
  delete test_window;
 | 
			
		||||
  delete shape_renderer_instance;
 | 
			
		||||
  delete txt_render_instance;
 | 
			
		||||
  SDL_GL_DestroyContext(context);
 | 
			
		||||
 | 
			
		||||
@ -14,15 +14,22 @@
 | 
			
		||||
Terminal::Terminal(void) {
 | 
			
		||||
  /* Placeholder welcome message to history. */
 | 
			
		||||
  _history.push_back("Welcome to Bettola (local mode)");
 | 
			
		||||
  _network = nullptr; /* No network connection for now. */
 | 
			
		||||
 | 
			
		||||
  _network      = new ClientNetwork();
 | 
			
		||||
  _is_remote    = false;
 | 
			
		||||
  _should_close = false;
 | 
			
		||||
 | 
			
		||||
  /* Create local file system for the client. */
 | 
			
		||||
  _local_vfs = vfs_manager::create_root_system();
 | 
			
		||||
  _local_vfs = vfs_manager::create_root_system("local");
 | 
			
		||||
  _local_cmd_processor = new CommandProcessor(_local_vfs);
 | 
			
		||||
  _current_path = get_full_path(_local_cmd_processor->get_current_dir());
 | 
			
		||||
  _current_path  = get_full_path(_local_cmd_processor->get_current_dir());
 | 
			
		||||
  _scroll_offset = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Terminal::close(void) {
 | 
			
		||||
  return _should_close;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Terminal::~Terminal(void) {
 | 
			
		||||
  if(_network) {
 | 
			
		||||
    delete _network;
 | 
			
		||||
@ -31,35 +38,57 @@ Terminal::~Terminal(void) {
 | 
			
		||||
 | 
			
		||||
void Terminal::_on_ret_press(void) {
 | 
			
		||||
  std::string command = _input_buffer;
 | 
			
		||||
  _input_buffer.clear(); /* Clear the input buffer immediately. */
 | 
			
		||||
 | 
			
		||||
  /* Add the command to history. */
 | 
			
		||||
  _history.push_back(_current_path + "> " + command);
 | 
			
		||||
 | 
			
		||||
  std::string response;
 | 
			
		||||
  /*
 | 
			
		||||
   * All commands run locally for now.
 | 
			
		||||
   * TODO: Remote commands.
 | 
			
		||||
   */
 | 
			
		||||
  if(command == "clear") {
 | 
			
		||||
    _history.clear();
 | 
			
		||||
  } else {
 | 
			
		||||
    response = _local_cmd_processor->process_command(command);
 | 
			
		||||
  }
 | 
			
		||||
  if(_is_remote) {
 | 
			
		||||
    /* REMOTE STATE. */
 | 
			
		||||
    _network->send_command(command.c_str());
 | 
			
		||||
 | 
			
		||||
  /* Check if the response is a new path for the prompt. */
 | 
			
		||||
  if(response.rfind("/", 0) == 0) {
 | 
			
		||||
    _current_path = response;
 | 
			
		||||
  } else if(!response.empty()) {
 | 
			
		||||
    /* Otherwise, it's command out. Split it by newline and add to history. */
 | 
			
		||||
    std::stringstream ss(response);
 | 
			
		||||
    std::string line;
 | 
			
		||||
    while(std::getline(ss, line, '\n')) {
 | 
			
		||||
      if(!line.empty()) {
 | 
			
		||||
        _history.push_back(line);
 | 
			
		||||
    if(command == "exit") {
 | 
			
		||||
      /* Don't wait for a response, the server booted us. */
 | 
			
		||||
      _is_remote = false;
 | 
			
		||||
      _network->disconnect();
 | 
			
		||||
      _current_path = get_full_path(_local_cmd_processor->get_current_dir());
 | 
			
		||||
      _history.push_back("Connection closed.");
 | 
			
		||||
    } else {
 | 
			
		||||
      /* For all other commands, wait for a response. */
 | 
			
		||||
      std::string response = _network->receive_response();
 | 
			
		||||
      if(!response.empty()) { _history.push_back(response); }
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    /* LOCAL STATE! */
 | 
			
		||||
    if(command.rfind("ssh ", 0) == 0) {
 | 
			
		||||
      if(_network->connect("localhost", 8080)) {
 | 
			
		||||
        _network->send_command(command.c_str());
 | 
			
		||||
        std::string response = _network->receive_response();
 | 
			
		||||
        if(response.rfind("CONNECTED ", 0) == 0) {
 | 
			
		||||
          _is_remote = true;
 | 
			
		||||
          std::string ip = response.substr(10);
 | 
			
		||||
          _current_path = "root@" + ip;
 | 
			
		||||
        } else {
 | 
			
		||||
          _history.push_back(response);
 | 
			
		||||
          _network->disconnect(); /* Disconnect on fialed ssh login. */
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        _history.push_back("Connection failed.");
 | 
			
		||||
      }
 | 
			
		||||
    } else if(command == "exit") {
 | 
			
		||||
      _should_close = true;
 | 
			
		||||
    } else if(command == "clear") {
 | 
			
		||||
      _history.clear();
 | 
			
		||||
    } else {
 | 
			
		||||
      std::string response = _local_cmd_processor->process_command(command);
 | 
			
		||||
      if(response.rfind("/", 0) == 0) {
 | 
			
		||||
        _current_path = response;
 | 
			
		||||
      } else if(!response.empty()) {
 | 
			
		||||
        _history.push_back(response);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _input_buffer.clear();
 | 
			
		||||
  /* TODO: Ugly hack. Refactor to pass window height
 | 
			
		||||
   * We need the window height to know if we should
 | 
			
		||||
   * auto-scroll, but we don't have it here.
 | 
			
		||||
 | 
			
		||||
@ -16,14 +16,17 @@ public:
 | 
			
		||||
  void handle_input(SDL_Event* event);
 | 
			
		||||
  void render(TextRenderer* renderer, int x, int y, int width, int height, bool show_cursor);
 | 
			
		||||
  void scroll(int amount, int content_height);
 | 
			
		||||
  bool close(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void _on_ret_press(void);
 | 
			
		||||
 | 
			
		||||
  std::string _input_buffer;
 | 
			
		||||
  bool _should_close;
 | 
			
		||||
  std::vector<std::string> _history;
 | 
			
		||||
  int _scroll_offset;
 | 
			
		||||
  std::string _current_path;
 | 
			
		||||
  bool _is_remote;
 | 
			
		||||
  vfs_node* _local_vfs;
 | 
			
		||||
  CommandProcessor* _local_cmd_processor;
 | 
			
		||||
  ClientNetwork* _network;
 | 
			
		||||
 | 
			
		||||
@ -4,14 +4,19 @@
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "terminal.h"
 | 
			
		||||
#include "ui/ui_window.h"
 | 
			
		||||
 | 
			
		||||
Desktop::Desktop(void) {
 | 
			
		||||
  _focused_window = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Desktop won't own the windows, so doesn't delete them. */
 | 
			
		||||
Desktop::~Desktop(void) {}
 | 
			
		||||
/* Desktop owns UIWindow, make sure we delete them. */
 | 
			
		||||
Desktop::~Desktop(void) {
 | 
			
		||||
  for(auto win : _windows) {
 | 
			
		||||
    delete win;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::add_window(UIWindow* window) {
 | 
			
		||||
  _windows.push_back(window);
 | 
			
		||||
@ -51,6 +56,20 @@ void Desktop::handle_event(SDL_Event* event) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::update(void) {
 | 
			
		||||
  _windows.erase(std::remove_if(_windows.begin(), _windows.end(),
 | 
			
		||||
                                [](UIWindow* window) {
 | 
			
		||||
                                  Terminal* term = window->get_content();
 | 
			
		||||
                                  if(term && term->close()) {
 | 
			
		||||
                                    /* Window owns the terminal content, delete window also deletes terminal. */
 | 
			
		||||
                                    delete window;
 | 
			
		||||
                                    return true; /* Remove from vector. */
 | 
			
		||||
                                  }
 | 
			
		||||
                                  return false;
 | 
			
		||||
                                }),
 | 
			
		||||
                              _windows.end());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
                     int screen_height, bool show_cursor) {
 | 
			
		||||
  for(auto win: _windows) {
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  void add_window(UIWindow* window);
 | 
			
		||||
  void handle_event(SDL_Event* event);
 | 
			
		||||
  void update(void);
 | 
			
		||||
  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height,
 | 
			
		||||
              bool show_cursor);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,12 @@ UIWindow::UIWindow(const char* title, int x, int y, int width, int height) {
 | 
			
		||||
  _is_focused   = false; /* Not focused by default? */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UIWindow::~UIWindow(void) {
 | 
			
		||||
  if(_content) {
 | 
			
		||||
    delete _content;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UIWindow::set_content(Terminal* term) {
 | 
			
		||||
  _content = term;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@
 | 
			
		||||
class UIWindow {
 | 
			
		||||
public:
 | 
			
		||||
  UIWindow(const char* title, int x, int y, int width, int height);
 | 
			
		||||
  ~UIWindow(void);
 | 
			
		||||
 | 
			
		||||
  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height,
 | 
			
		||||
              bool show_cursor);
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ vfs_node* new_node(std::string name, vfs_node_type type, vfs_node* parent) {
 | 
			
		||||
  return node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vfs_node* vfs_manager::create_root_system(void) {
 | 
			
		||||
vfs_node* vfs_manager::create_root_system(const std::string& system_type) {
 | 
			
		||||
  /* Rood directory. */
 | 
			
		||||
  vfs_node* root = new_node("/", DIR_NODE, nullptr);
 | 
			
		||||
 | 
			
		||||
@ -33,5 +33,12 @@ vfs_node* vfs_manager::create_root_system(void) {
 | 
			
		||||
  ls_exe->content = "[executable]";
 | 
			
		||||
  bin->children["ls"] = ls_exe;
 | 
			
		||||
 | 
			
		||||
  if(system_type == "npc") {
 | 
			
		||||
    vfs_node* npc_file = new_node("npc_system.txt", FILE_NODE, root);
 | 
			
		||||
    npc_file->content = "This guy sucks nuts!";
 | 
			
		||||
    root->children["npc_system.txt"] = npc_file;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return root;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,5 +3,5 @@
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
namespace vfs_manager {
 | 
			
		||||
  vfs_node* create_root_system(void);
 | 
			
		||||
  vfs_node* create_root_system(const std::string& system_type);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,16 +1,22 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include "vfs_manager.h"
 | 
			
		||||
#include "network_manager.h"
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv) {
 | 
			
		||||
  printf("=== Server starting ===\n");
 | 
			
		||||
 | 
			
		||||
  vfs_node* root_vfs = vfs_manager::create_root_system();
 | 
			
		||||
  printf("Virtual file system created. Root contains '%s' directory.\n",
 | 
			
		||||
         root_vfs->children["home"]->name.c_str());
 | 
			
		||||
  /* Create the world map of networks. */
 | 
			
		||||
  std::map<std::string, vfs_node*> world_vfs;
 | 
			
		||||
  world_vfs["8.8.8.8"] = vfs_manager::create_root_system("npc"); /* Basic npc system. */
 | 
			
		||||
  world_vfs["10.0.2.15"] = vfs_manager::create_root_system("npc"); /* Another one. */
 | 
			
		||||
  printf("Created world with %zu networks.\n", world_vfs.size());
 | 
			
		||||
 | 
			
		||||
  NetworkManager* net_manager = new NetworkManager(root_vfs);
 | 
			
		||||
  /* Network manager now managed the whole world. */
 | 
			
		||||
  NetworkManager* net_manager = new NetworkManager(world_vfs);
 | 
			
		||||
  net_manager->start(); /* Our loop. */
 | 
			
		||||
 | 
			
		||||
  delete net_manager; /* Shouldn't get here. */
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,15 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <SDL_net.h>
 | 
			
		||||
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
 | 
			
		||||
#include "network_manager.h"
 | 
			
		||||
 | 
			
		||||
NetworkManager::NetworkManager(vfs_node* vfs_root) {
 | 
			
		||||
  _vfs_root = vfs_root;
 | 
			
		||||
NetworkManager::NetworkManager(std::map<std::string, vfs_node*> world_vfs) {
 | 
			
		||||
  _world_vfs = world_vfs;
 | 
			
		||||
 | 
			
		||||
  if(!NET_Init()) {
 | 
			
		||||
    printf("Error: SDLNet_Init: %s\n", SDL_GetError());
 | 
			
		||||
@ -45,7 +47,7 @@ void NetworkManager::_handle_new_connections(void) {
 | 
			
		||||
  if(NET_AcceptClient(_server_socket, &client_socket)) {
 | 
			
		||||
    if(client_socket) {
 | 
			
		||||
      Player* new_player = new Player(client_socket);
 | 
			
		||||
      new_player->current_dir = _vfs_root;
 | 
			
		||||
      new_player->cmd_processor = nullptr; /* Not connected to remote host yet. */
 | 
			
		||||
      _players.push_back(new_player);
 | 
			
		||||
      printf("Client connected. Total players: %zu\n", _players.size());
 | 
			
		||||
    }
 | 
			
		||||
@ -63,72 +65,65 @@ void NetworkManager::_handle_client_activity(void) {
 | 
			
		||||
   * 0 if no data, and < 0 on error (disconnect).
 | 
			
		||||
   */
 | 
			
		||||
    int bytes_received = NET_ReadFromStreamSocket(player->socket, buffer, 512);
 | 
			
		||||
    if(bytes_received > 0) {
 | 
			
		||||
      printf("bytes_received: %d\n", bytes_received);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(bytes_received > 0) {
 | 
			
		||||
      _process_command(player, buffer);
 | 
			
		||||
    } else if(bytes_received < 0 ) {
 | 
			
		||||
      /* _process_command returns true, the client wants to exit, boot it! */
 | 
			
		||||
      if(_process_command(player, buffer)) {
 | 
			
		||||
        _disconnect_client(player, i);
 | 
			
		||||
      }
 | 
			
		||||
    } else if(bytes_received < 0) {
 | 
			
		||||
      /* Disconnect on error or if the client closes the connection. */
 | 
			
		||||
      _disconnect_client(player, i);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetworkManager::_process_command(Player* player, char* command) {
 | 
			
		||||
  /* Remove trailing newline for command comparison. */
 | 
			
		||||
bool NetworkManager::_process_command(Player* player, char* command) {
 | 
			
		||||
  /* Create a clean std::string from the buffer. */
 | 
			
		||||
  std::string cmd_str = command;
 | 
			
		||||
  if(!cmd_str.empty() && cmd_str.back() == '\n') {
 | 
			
		||||
    cmd_str.pop_back();
 | 
			
		||||
  }
 | 
			
		||||
  if(strncmp(command, "cd ", 3) == 0) {
 | 
			
		||||
    /* Handle 'cd' command. */
 | 
			
		||||
    std::string target_dir_name = command + 3;
 | 
			
		||||
    /* Remove trailing newline if it exists. */
 | 
			
		||||
    if(!target_dir_name.empty() && target_dir_name.back() == '\n') {
 | 
			
		||||
      target_dir_name.pop_back();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(target_dir_name == "..") {
 | 
			
		||||
      if(player->current_dir->parent) {
 | 
			
		||||
        player->current_dir = player->current_dir->parent;
 | 
			
		||||
      }
 | 
			
		||||
    } else if(player->current_dir->children.count(target_dir_name)) {
 | 
			
		||||
      vfs_node* target_node = player->current_dir->children[target_dir_name];
 | 
			
		||||
      if(target_node->type == DIR_NODE) {
 | 
			
		||||
        player->current_dir = target_node;
 | 
			
		||||
      } else {
 | 
			
		||||
        /* It's a file, not a directory. */
 | 
			
		||||
        NET_WriteToStreamSocket(player->socket, "cd: not a directory\n", 22);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
  if(cmd_str == "exit") {
 | 
			
		||||
    /* Client is disconnecting, no response needed. */
 | 
			
		||||
    return true; /* Disconnect signal. */
 | 
			
		||||
  } else if(strncmp(cmd_str.c_str(), "ssh ", 4) == 0) {
 | 
			
		||||
    std::string target = cmd_str.substr(4);
 | 
			
		||||
    std::string target_ip = target;
 | 
			
		||||
    size_t at_pos = target.find('@');
 | 
			
		||||
    if(at_pos != std::string::npos) {
 | 
			
		||||
      target_ip = target.substr(at_pos + 1);
 | 
			
		||||
    }
 | 
			
		||||
    /* On successful cd, send back the new path for the prompt. */
 | 
			
		||||
    std::string new_path = get_full_path(player->current_dir);
 | 
			
		||||
    NET_WriteToStreamSocket(player->socket, new_path.c_str(), new_path.length()+1);
 | 
			
		||||
  } else if(cmd_str == "ls") {
 | 
			
		||||
    std::string response = "";
 | 
			
		||||
    if(player->current_dir && player->current_dir->type == DIR_NODE) {
 | 
			
		||||
      for(auto const& [name, node] : player->current_dir->children) {
 | 
			
		||||
        response += name;
 | 
			
		||||
        if(node->type == DIR_NODE) {
 | 
			
		||||
          response += "/";
 | 
			
		||||
        }
 | 
			
		||||
        response += "  ";
 | 
			
		||||
      }
 | 
			
		||||
    if(_world_vfs.count(target_ip)) {
 | 
			
		||||
      /* Create new command processor for the player's session on the target VFS. */
 | 
			
		||||
      if(player->cmd_processor) { delete player->cmd_processor; }
 | 
			
		||||
      player->cmd_processor = new CommandProcessor(_world_vfs[target_ip]);
 | 
			
		||||
      std::string response = "CONNECTED " + target_ip;
 | 
			
		||||
      NET_WriteToStreamSocket(player->socket, response.c_str(), response.length()+1);
 | 
			
		||||
    } else {
 | 
			
		||||
      NET_WriteToStreamSocket(player->socket, "ssh: Could not resolve hostname\n", 32);
 | 
			
		||||
    }
 | 
			
		||||
    /* NET_WriteToStreamSocket is essentially the new send function in SDL3. */
 | 
			
		||||
    NET_WriteToStreamSocket(player->socket, response.c_str(), response.length()+1);
 | 
			
		||||
  } else if(cmd_str == "clear") {
 | 
			
		||||
    /* Send an empty reply to unblock. */
 | 
			
		||||
    NET_WriteToStreamSocket(player->socket, "", 1);
 | 
			
		||||
  } else {
 | 
			
		||||
    /* Unknown command. */
 | 
			
		||||
    std::string response = "Unknown command: " + cmd_str + "\n";
 | 
			
		||||
    NET_WriteToStreamSocket(player->socket, response.c_str(), response.length()+1);
 | 
			
		||||
    /* If not ssh command, and we have a command processor, process it. */
 | 
			
		||||
    if(player->cmd_processor) {
 | 
			
		||||
      std::string response = player->cmd_processor->process_command(cmd_str);
 | 
			
		||||
      printf("OK: cmd_processor exits. Processing command '%s'\n", cmd_str.c_str());
 | 
			
		||||
      NET_WriteToStreamSocket(player->socket, response.c_str(), response.length()+1);
 | 
			
		||||
    } else {
 | 
			
		||||
      printf("ERROR: cmd_processor is null. Cannot process command '%s'\n", cmd_str.c_str());
 | 
			
		||||
      NET_WriteToStreamSocket(player->socket, "Error: Not in a remote session.\n", 32);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return false; /* Don't disconnect. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetworkManager::_disconnect_client(Player* player, int index) {
 | 
			
		||||
  printf("Client disconnected.\n");
 | 
			
		||||
  NET_DestroyStreamSocket(player->socket);
 | 
			
		||||
  _players.erase(_players.begin() + index);
 | 
			
		||||
  /* If we have a valid index, remove the player. Otherwise, find them. */
 | 
			
		||||
  if(index != -1) {
 | 
			
		||||
    _players.erase(_players.begin() + index);
 | 
			
		||||
  }
 | 
			
		||||
  if(player->cmd_processor) { delete player->cmd_processor; }
 | 
			
		||||
  delete player;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,14 +1,15 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <SDL3/SDL.h>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
#include "player.h"
 | 
			
		||||
 | 
			
		||||
class NetworkManager {
 | 
			
		||||
public:
 | 
			
		||||
  NetworkManager(vfs_node* vfs_root);
 | 
			
		||||
  NetworkManager(std::map<std::string, vfs_node*> world_vfs);
 | 
			
		||||
  ~NetworkManager(void);
 | 
			
		||||
 | 
			
		||||
  void start(void);
 | 
			
		||||
@ -16,10 +17,10 @@ public:
 | 
			
		||||
private:
 | 
			
		||||
  void _handle_new_connections(void);
 | 
			
		||||
  void _handle_client_activity(void);
 | 
			
		||||
  void _process_command(Player* player, char* command);
 | 
			
		||||
  bool _process_command(Player* player, char* command);
 | 
			
		||||
  void _disconnect_client(Player* player, int index);
 | 
			
		||||
 | 
			
		||||
  NET_Server* _server_socket;
 | 
			
		||||
  std::vector<Player*> _players;
 | 
			
		||||
  vfs_node* _vfs_root;
 | 
			
		||||
  std::map<std::string, vfs_node*> _world_vfs;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -3,5 +3,5 @@
 | 
			
		||||
 | 
			
		||||
Player::Player(NET_StreamSocket* new_socket) {
 | 
			
		||||
  socket = new_socket;
 | 
			
		||||
  current_dir = nullptr; /* Will set to VFS root on connection. */
 | 
			
		||||
  cmd_processor = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,15 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <SDL_net.h>
 | 
			
		||||
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
 | 
			
		||||
class Player {
 | 
			
		||||
public:
 | 
			
		||||
  Player(NET_StreamSocket* socket);
 | 
			
		||||
 | 
			
		||||
  NET_StreamSocket* socket;
 | 
			
		||||
  vfs_node* current_dir;
 | 
			
		||||
  CommandProcessor* cmd_processor; /* Manages the VFS state for the remote session. */
 | 
			
		||||
private:
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user