[Change] Remove hybrid local/remote network model
Client architecture has been refactored to be fully server-authoritative, remove the previous "hybrid" model that supported both local and remote command execution, that was a stupid idea. - Client now connects to server on startup. - The local command processor and VFS have been removed from the Terminal class. - All command processing is now handled by the server. - The client is now just a thin client essentially I'll in the future enable single player mode by running the server on the local machine in a separate thread.
This commit is contained in:
		
							parent
							
								
									a502fa64cf
								
							
						
					
					
						commit
						388c6429cf
					
				
							
								
								
									
										4
									
								
								assets/scripts/bin/ssh.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								assets/scripts/bin/ssh.lua
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,4 @@
 | 
			
		||||
-- /bin/ssh - Connects to a remote host.
 | 
			
		||||
local target = arg[1]
 | 
			
		||||
if not target then return "ssh: missing operand" end
 | 
			
		||||
return { action = "ssh", target = target }
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <SDL3_net/SDL_net.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "SDL3_net/SDL_net.h"
 | 
			
		||||
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
#include <SDL3/SDL_error.h>
 | 
			
		||||
 | 
			
		||||
ClientNetwork::ClientNetwork(void) {
 | 
			
		||||
  if(!NET_Init()) {
 | 
			
		||||
@ -55,9 +55,9 @@ bool ClientNetwork::connect(const char* host, int port) {
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ClientNetwork::send_command(const char* command) {
 | 
			
		||||
void ClientNetwork::send_command(const std::string& command) {
 | 
			
		||||
  if(!_socket) return;
 | 
			
		||||
  NET_WriteToStreamSocket(_socket, command, strlen(command)+1);
 | 
			
		||||
  NET_WriteToStreamSocket(_socket, command.c_str(), command.length()+1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ClientNetwork::disconnect(void) {
 | 
			
		||||
@ -80,6 +80,7 @@ std::string ClientNetwork::receive_response(void) {
 | 
			
		||||
      return std::string(buffer);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return ""; /* Return empty on timeout or error. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -9,7 +9,7 @@ public:
 | 
			
		||||
  ~ClientNetwork(void);
 | 
			
		||||
 | 
			
		||||
  bool connect(const char* host, int port);
 | 
			
		||||
  void send_command(const char* command);
 | 
			
		||||
  void send_command(const std::string& data);
 | 
			
		||||
  void disconnect(void);
 | 
			
		||||
  std::string receive_response(void);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -4,26 +4,26 @@
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "vfs_manager.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include "terminal.h"
 | 
			
		||||
 | 
			
		||||
Terminal::Terminal(void) {
 | 
			
		||||
  /* Placeholder welcome message to history. */
 | 
			
		||||
  _history.push_back("Welcome to Bettola (local mode)");
 | 
			
		||||
  _history.push_back("Connecting to server...");
 | 
			
		||||
 | 
			
		||||
  _network      = new ClientNetwork();
 | 
			
		||||
  _is_remote    = false;
 | 
			
		||||
  _should_close = false;
 | 
			
		||||
 | 
			
		||||
  /* Create local file system for the client. */
 | 
			
		||||
  VFSManager vfs_manager;
 | 
			
		||||
  _local_vfs = vfs_manager.create_vfs("local");
 | 
			
		||||
  _local_cmd_processor = new CommandProcessor(_local_vfs);
 | 
			
		||||
  _current_path  = get_full_path(_local_cmd_processor->get_current_dir());
 | 
			
		||||
  _scroll_offset = 0;
 | 
			
		||||
 | 
			
		||||
  /* TODO: Move network to main.cpp, or better yet, a dedicatated game state file */
 | 
			
		||||
  if(_network->connect("localhost", 8080)) {
 | 
			
		||||
    _history.push_back("Connection successful.");
 | 
			
		||||
    _prompt = _network->receive_response(); /* Get initial prompt from server. */
 | 
			
		||||
  } else {
 | 
			
		||||
    _history.push_back("Connection failed. Please restart.");
 | 
			
		||||
    _prompt = "error>";
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Terminal::close(void) {
 | 
			
		||||
@ -41,52 +41,27 @@ void Terminal::_on_ret_press(void) {
 | 
			
		||||
  _input_buffer.clear(); /* Clear the input buffer immediately. */
 | 
			
		||||
 | 
			
		||||
  /* Add the command to history. */
 | 
			
		||||
  _history.push_back(_current_path + "> " + command);
 | 
			
		||||
  _history.push_back(_prompt + "> " + command);
 | 
			
		||||
 | 
			
		||||
  if(_is_remote) {
 | 
			
		||||
    /* REMOTE STATE. */
 | 
			
		||||
    _network->send_command(command.c_str());
 | 
			
		||||
  if(command == "exit") {
 | 
			
		||||
    _should_close = true;
 | 
			
		||||
    /* TODO: Window will close, and the OS will close the socket.
 | 
			
		||||
     * server will detect the disconnection.
 | 
			
		||||
     */
 | 
			
		||||
    return;
 | 
			
		||||
  } else if(command == "clear"){
 | 
			
		||||
    _history.clear();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  _network->send_command(command);
 | 
			
		||||
  std::string response = _network->receive_response();
 | 
			
		||||
  /* Server sends back "output\nprompt". Split them. */
 | 
			
		||||
  size_t last_newline = response.find_last_of('\n');
 | 
			
		||||
  if(last_newline != std::string::npos) {
 | 
			
		||||
    _prompt = response.substr(last_newline+1);
 | 
			
		||||
    std::string output = response.substr(0, last_newline);
 | 
			
		||||
    if(!output.empty()) { _history.push_back(output); }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* TODO: Ugly hack. Refactor to pass window height
 | 
			
		||||
@ -151,7 +126,7 @@ void Terminal::render(TextRenderer* renderer, int x, int y, int width, int heigh
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Draw current input line. */
 | 
			
		||||
  std::string line_to_render = _current_path + "> " + _input_buffer;
 | 
			
		||||
  std::string line_to_render = _prompt + "> " + _input_buffer;
 | 
			
		||||
  if(show_cursor) {
 | 
			
		||||
    line_to_render += "_";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -5,8 +5,6 @@
 | 
			
		||||
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
#include "vfs_manager.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
 | 
			
		||||
class Terminal {
 | 
			
		||||
public:
 | 
			
		||||
@ -25,9 +23,6 @@ private:
 | 
			
		||||
  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;
 | 
			
		||||
  std::string _prompt;
 | 
			
		||||
  ClientNetwork* _network;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
#include "lua_processor.h"
 | 
			
		||||
#include <sol/forward.hpp>
 | 
			
		||||
#include <sol/object.hpp>
 | 
			
		||||
#include <sol/protected_function_result.hpp>
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
LuaProcessor::LuaProcessor(void) {
 | 
			
		||||
@ -27,7 +29,13 @@ sol::object LuaProcessor::execute(const std::string& script, vfs_node* current_d
 | 
			
		||||
    }
 | 
			
		||||
    _lua["arg"] = arg_table;
 | 
			
		||||
 | 
			
		||||
    return _lua.script(script);
 | 
			
		||||
    sol::protected_function_result result = _lua.safe_script(script);
 | 
			
		||||
    if(result.valid()) {
 | 
			
		||||
      return result;
 | 
			
		||||
    } else {
 | 
			
		||||
      sol::error err = result;
 | 
			
		||||
      return sol::make_object(_lua, err.what());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  } catch(const sol::error& e) {
 | 
			
		||||
    /* Return the error message as a string in a sol::object. */
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,7 @@ vfs_node* VFSManager::create_vfs(const std::string& system_type) {
 | 
			
		||||
  user->children["readme.txt"] = readme;
 | 
			
		||||
 | 
			
		||||
  /* Link to the shared directories from the template. */
 | 
			
		||||
  root->children["bin"] = _vfs_root->children["bin"];
 | 
			
		||||
  root->children["bin"] = copy_vfs_node(_vfs_root->children["bin"], root);
 | 
			
		||||
 | 
			
		||||
  if(system_type == "npc") {
 | 
			
		||||
    vfs_node* npc_file = new_node("npc_system.txt", FILE_NODE, root);
 | 
			
		||||
 | 
			
		||||
@ -1,24 +1,11 @@
 | 
			
		||||
#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");
 | 
			
		||||
 | 
			
		||||
  VFSManager vfs_manager;
 | 
			
		||||
 | 
			
		||||
  /* Create the world map of networks. */
 | 
			
		||||
  std::map<std::string, vfs_node*> world_vfs;
 | 
			
		||||
  world_vfs["8.8.8.8"] = vfs_manager.create_vfs("npc"); /* Basic npc system. */
 | 
			
		||||
  world_vfs["10.0.2.15"] = vfs_manager.create_vfs("npc"); /* Another one. */
 | 
			
		||||
  printf("Created world with %zu networks.\n", world_vfs.size());
 | 
			
		||||
 | 
			
		||||
  /* Network manager now managed the whole world. */
 | 
			
		||||
  NetworkManager* net_manager = new NetworkManager(world_vfs);
 | 
			
		||||
  NetworkManager* net_manager = new NetworkManager();
 | 
			
		||||
  net_manager->start(); /* Our loop. */
 | 
			
		||||
 | 
			
		||||
  delete net_manager; /* Shouldn't get here. */
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,26 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <SDL3_net/SDL_net.h>
 | 
			
		||||
 | 
			
		||||
#include "SDL3_net/SDL_net.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include "vfs_manager.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
 | 
			
		||||
#include "network_manager.h"
 | 
			
		||||
 | 
			
		||||
NetworkManager::NetworkManager(std::map<std::string, vfs_node*> world_vfs) {
 | 
			
		||||
  _world_vfs = world_vfs;
 | 
			
		||||
/* Send a length-prefixed string. */
 | 
			
		||||
void send_response_string(NET_StreamSocket* socket, const std::string& data) {
 | 
			
		||||
  if(!socket) return;
 | 
			
		||||
  NET_WriteToStreamSocket(socket, data.c_str(), data.length()+1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NetworkManager::NetworkManager(void) {
 | 
			
		||||
  /* The VFSManager for the world's NPC's.
 | 
			
		||||
   * Player VSManagers will be created on a per-player basis.
 | 
			
		||||
   */
 | 
			
		||||
  _world_vfs["8.8.8.8"]   = _npc_vfs_manager.create_vfs("npc");
 | 
			
		||||
  _world_vfs["10.0.2.15"] = _npc_vfs_manager.create_vfs("npc");
 | 
			
		||||
  printf("Created world with %zu networks.\n", _world_vfs.size());
 | 
			
		||||
 | 
			
		||||
  if(!NET_Init()) {
 | 
			
		||||
    printf("Error: SDLNet_Init: %s\n", SDL_GetError());
 | 
			
		||||
@ -24,6 +35,9 @@ NetworkManager::NetworkManager(std::map<std::string, vfs_node*> world_vfs) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NetworkManager::~NetworkManager(void) {
 | 
			
		||||
  for(auto player : _players) {
 | 
			
		||||
    delete player;
 | 
			
		||||
  }
 | 
			
		||||
  NET_DestroyServer(_server_socket);
 | 
			
		||||
  NET_Quit();
 | 
			
		||||
}
 | 
			
		||||
@ -47,83 +61,80 @@ 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->cmd_processor = nullptr; /* Not connected to remote host yet. */
 | 
			
		||||
      _players.push_back(new_player);
 | 
			
		||||
      printf("Client connected. Total players: %zu\n", _players.size());
 | 
			
		||||
 | 
			
		||||
      /* Send the initial prompt to the new client. */
 | 
			
		||||
      std::string prompt = get_full_path(new_player->cmd_processor->get_current_dir());
 | 
			
		||||
      send_response_string(client_socket, prompt);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetworkManager::_handle_client_activity(void) {
 | 
			
		||||
  char buffer [512];
 | 
			
		||||
 | 
			
		||||
  /* Iterate backwards so we can safely remove disconnected clients. */
 | 
			
		||||
  for(int i = _players.size()-1; i >= 0; --i) {
 | 
			
		||||
    Player* player = _players[i];
 | 
			
		||||
    NET_StreamSocket* sock_array[1] = { player->socket };
 | 
			
		||||
 | 
			
		||||
  /* NET_ReadFromStreamSocket returns > 0 if data was received.
 | 
			
		||||
   * 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);
 | 
			
		||||
    /* Timeout of 0 for non-blocking check. */
 | 
			
		||||
    int ready = NET_WaitUntilInputAvailable((void**)sock_array, 1, 0);
 | 
			
		||||
    if(ready == -1) {
 | 
			
		||||
      /* An error occured on the socket. */
 | 
			
		||||
      fprintf(stderr, "[SERVER] Socket error, disconnecting client.\n");
 | 
			
		||||
      _disconnect_client(player, i);
 | 
			
		||||
      continue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if(bytes_received > 0) {
 | 
			
		||||
      /* _process_command returns true, the client wants to exit, boot it! */
 | 
			
		||||
      if(_process_command(player, buffer)) {
 | 
			
		||||
    if(ready > 0) {
 | 
			
		||||
      /* Socket has data. If processing fails, disconnect the client. */
 | 
			
		||||
      fprintf(stderr, "[SERVER] Socket has data, processing command...\n");
 | 
			
		||||
 | 
			
		||||
      if(!_process_command(player)) {
 | 
			
		||||
        fprintf(stderr, "[SERVER] _process_command failed, disconnecting client.\n");
 | 
			
		||||
        _disconnect_client(player, i);
 | 
			
		||||
      }
 | 
			
		||||
    } else if(bytes_received < 0) {
 | 
			
		||||
      /* Disconnect on error or if the client closes the connection. */
 | 
			
		||||
      _disconnect_client(player, i);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool NetworkManager::_process_command(Player* player, char* command) {
 | 
			
		||||
  /* Create a clean std::string from the buffer. */
 | 
			
		||||
  std::string cmd_str = command;
 | 
			
		||||
bool NetworkManager::_process_command(Player* player) {
 | 
			
		||||
  /* Read the length-prefixed command from the client. */
 | 
			
		||||
  char buffer[2048];
 | 
			
		||||
  memset(buffer, 0, sizeof(buffer));
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
    }
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    /* 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);
 | 
			
		||||
    }
 | 
			
		||||
  int bytes_received = NET_ReadFromStreamSocket(player->socket, buffer, sizeof(buffer)-1);
 | 
			
		||||
  if(bytes_received <= 0) {
 | 
			
		||||
    return false; /* Error or disconnect. */
 | 
			
		||||
  }
 | 
			
		||||
  return false; /* Don't disconnect. */
 | 
			
		||||
 | 
			
		||||
  std::string cmd_str(buffer);
 | 
			
		||||
  fprintf(stderr, "[SERVER] Received command: \"%s\"\n", cmd_str.c_str());
 | 
			
		||||
 | 
			
		||||
  /* Process the command. */
 | 
			
		||||
  std::string response;
 | 
			
		||||
 | 
			
		||||
  if(player->cmd_processor) {
 | 
			
		||||
    response = player->cmd_processor->process_command(cmd_str);
 | 
			
		||||
  } else {
 | 
			
		||||
    response = "Error: No command processor available for this player.";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Get the new prompt. Append it to response with a known separator. */
 | 
			
		||||
  std::string new_prompt = get_full_path(player->cmd_processor->get_current_dir());
 | 
			
		||||
  std::string final_response = response + "\n" + new_prompt;
 | 
			
		||||
 | 
			
		||||
  send_response_string(player->socket, final_response);
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetworkManager::_disconnect_client(Player* player, int index) {
 | 
			
		||||
  fprintf(stderr, "[SERVER] Disconnecting client.\n");
 | 
			
		||||
  NET_DestroyStreamSocket(player->socket);
 | 
			
		||||
  /* 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;
 | 
			
		||||
  printf("Client disconnected. Total players: %zu\n", _players.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,10 +6,11 @@
 | 
			
		||||
 | 
			
		||||
#include <SDL3_net/SDL_net.h>
 | 
			
		||||
#include "player.h"
 | 
			
		||||
#include "vfs_manager.h"
 | 
			
		||||
 | 
			
		||||
class NetworkManager {
 | 
			
		||||
public:
 | 
			
		||||
  NetworkManager(std::map<std::string, vfs_node*> world_vfs);
 | 
			
		||||
  NetworkManager();
 | 
			
		||||
  ~NetworkManager(void);
 | 
			
		||||
 | 
			
		||||
  void start(void);
 | 
			
		||||
@ -17,10 +18,11 @@ public:
 | 
			
		||||
private:
 | 
			
		||||
  void _handle_new_connections(void);
 | 
			
		||||
  void _handle_client_activity(void);
 | 
			
		||||
  bool _process_command(Player* player, char* command);
 | 
			
		||||
  bool _process_command(Player* player);
 | 
			
		||||
  void _disconnect_client(Player* player, int index);
 | 
			
		||||
 | 
			
		||||
  NET_Server* _server_socket;
 | 
			
		||||
  std::vector<Player*> _players;
 | 
			
		||||
  std::map<std::string, vfs_node*> _world_vfs;
 | 
			
		||||
  std::map<std::string, vfs_node*> _world_vfs; /* For NPC's. */
 | 
			
		||||
  VFSManager _npc_vfs_manager;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,15 @@
 | 
			
		||||
#include "player.h"
 | 
			
		||||
#include <SDL3_net/SDL_net.h>
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
 | 
			
		||||
Player::Player(NET_StreamSocket* new_socket) {
 | 
			
		||||
  socket = new_socket;
 | 
			
		||||
  cmd_processor = nullptr;
 | 
			
		||||
Player::Player(NET_StreamSocket* new_socket) :
 | 
			
		||||
    socket(new_socket),
 | 
			
		||||
    vfs_root(vfs_manager.create_vfs("player")),
 | 
			
		||||
    cmd_processor(new CommandProcessor(vfs_root)) {}
 | 
			
		||||
 | 
			
		||||
Player::~Player(void) {
 | 
			
		||||
  if(cmd_processor) {
 | 
			
		||||
    delete cmd_processor;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* TODO: The VFSManager should handle deleting the vfs_root. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,13 +3,17 @@
 | 
			
		||||
#include <SDL3_net/SDL_net.h>
 | 
			
		||||
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include "vfs_manager.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
 | 
			
		||||
class Player {
 | 
			
		||||
public:
 | 
			
		||||
  Player(NET_StreamSocket* socket);
 | 
			
		||||
  ~Player(void);
 | 
			
		||||
 | 
			
		||||
  NET_StreamSocket* socket;
 | 
			
		||||
  VFSManager vfs_manager;
 | 
			
		||||
  vfs_node* vfs_root;
 | 
			
		||||
  CommandProcessor* cmd_processor; /* Manages the VFS state for the remote session. */
 | 
			
		||||
private:
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user