[Add] Client/server networking and 'ls' command.
This commit is contained in:
		
							parent
							
								
									70f096abc4
								
							
						
					
					
						commit
						92106a3c44
					
				@ -5,10 +5,13 @@ add_executable(bettolac
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
find_package(SDL3 REQUIRED)
 | 
			
		||||
find_package(SDL3_net REQUIRED)
 | 
			
		||||
find_package(GLEW REQUIRED)
 | 
			
		||||
find_package(OpenGL REQUIRED)
 | 
			
		||||
find_package(Freetype REQUIRED)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(bettolac PRIVATE bettola SDL3::SDL3 GLEW::glew OpenGL::GL Freetype::Freetype)
 | 
			
		||||
target_link_libraries(bettolac PRIVATE bettola SDL3::SDL3 SDL3_net
 | 
			
		||||
  GLEW::glew OpenGL::GL Freetype::Freetype)
 | 
			
		||||
 | 
			
		||||
target_include_directories(bettolac PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
 | 
			
		||||
target_include_directories(bettolac PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src
 | 
			
		||||
  /usr/local/include/SDL3_net)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										76
									
								
								client/src/client_network.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								client/src/client_network.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
#include <SDL3/SDL_error.h>
 | 
			
		||||
 | 
			
		||||
ClientNetwork::ClientNetwork(void) {
 | 
			
		||||
  if(!NET_Init()) {
 | 
			
		||||
    printf("Error: NET_Init: %s\n", SDL_GetError());
 | 
			
		||||
  }
 | 
			
		||||
  _socket = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ClientNetwork::~ClientNetwork(void) {
 | 
			
		||||
  if(_socket) {
 | 
			
		||||
    NET_DestroyStreamSocket(_socket);
 | 
			
		||||
  }
 | 
			
		||||
  NET_Quit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ClientNetwork::connect(const char* host, int port) {
 | 
			
		||||
  NET_Address* address = NET_ResolveHostname(host);
 | 
			
		||||
  if(!address) {
 | 
			
		||||
    printf("Error: NET_ResolveHostname %s\n", SDL_GetError());
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* This seems impossible, but I'll try it..
 | 
			
		||||
   * Pass the port to CreateClient as the compiler insists we do.
 | 
			
		||||
   */
 | 
			
		||||
  /* FUCK ME!! Wait up to 5 seconds for DNS resolution to complete. */
 | 
			
		||||
  if(NET_WaitUntilResolved(address, 5000) <= 0) {
 | 
			
		||||
    printf("Error: NET_WaitUntilResolved: %s\n", SDL_GetError());
 | 
			
		||||
    NET_UnrefAddress(address);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _socket = NET_CreateClient(address, port);
 | 
			
		||||
  if(!_socket) {
 | 
			
		||||
    printf("Error: NET_CreateClient: %s\n", SDL_GetError());
 | 
			
		||||
    NET_UnrefAddress(address);
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Wait up to 5 seconds for the connection to establish. */
 | 
			
		||||
  if(NET_WaitUntilConnected((_socket), 5000) > 0) {
 | 
			
		||||
    printf("connected server.\n");
 | 
			
		||||
    NET_UnrefAddress(address);
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  printf("Error: connection timeout.\n");
 | 
			
		||||
  NET_UnrefAddress(address);
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ClientNetwork::send_command(const char* command) {
 | 
			
		||||
  if(!_socket) return;
 | 
			
		||||
  NET_WriteToStreamSocket(_socket, command, strlen(command)+1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ClientNetwork::receive_response(void) {
 | 
			
		||||
  if(!_socket) return "";
 | 
			
		||||
  char buffer[2048];
 | 
			
		||||
  memset(buffer, 0, sizeof(buffer));
 | 
			
		||||
 | 
			
		||||
  void* socket_ptr = _socket;
 | 
			
		||||
  if(NET_WaitUntilInputAvailable(&socket_ptr, 1, 2000) > 0) {
 | 
			
		||||
    if(NET_ReadFromStreamSocket(_socket, buffer, sizeof(buffer)) > 0) {
 | 
			
		||||
      return std::string(buffer);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return ""; /* Return empty on timeout or error. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								client/src/client_network.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								client/src/client_network.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <SDL_net.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
class ClientNetwork {
 | 
			
		||||
public:
 | 
			
		||||
  ClientNetwork(void);
 | 
			
		||||
  ~ClientNetwork(void);
 | 
			
		||||
 | 
			
		||||
  bool connect(const char* host, int port);
 | 
			
		||||
  void send_command(const char* command);
 | 
			
		||||
  std::string receive_response(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  NET_StreamSocket* _socket;
 | 
			
		||||
};
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
#include <GL/glew.h>
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "terminal.h"
 | 
			
		||||
 | 
			
		||||
@ -10,6 +11,16 @@ Terminal::Terminal(void) {
 | 
			
		||||
  /* Placeholder welcome message to history. */
 | 
			
		||||
  _history.push_back("Welcome to Bettola");
 | 
			
		||||
  _scroll_offset = 0;
 | 
			
		||||
  _network = new ClientNetwork();
 | 
			
		||||
  if(_network->connect("localhost", 8080)) {
 | 
			
		||||
    _history.push_back("Connection to server Success!");
 | 
			
		||||
  } else {
 | 
			
		||||
    _history.push_back("Connection to server failed!");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Terminal::~Terminal(void) {
 | 
			
		||||
  delete _network;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Terminal::_on_ret_press(void) {
 | 
			
		||||
@ -20,6 +31,11 @@ void Terminal::_on_ret_press(void) {
 | 
			
		||||
   * TODO: Parse and execute commands.
 | 
			
		||||
   */
 | 
			
		||||
  printf("Command entered: %s\n", _input_buffer.c_str());
 | 
			
		||||
  _network->send_command(_input_buffer.c_str());
 | 
			
		||||
  std::string response = _network->receive_response();
 | 
			
		||||
  if(!response.empty()) {
 | 
			
		||||
    _history.push_back(response);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if(_input_buffer == "clear") {
 | 
			
		||||
    _history.clear();
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,12 @@
 | 
			
		||||
#include <SDL3/SDL.h>
 | 
			
		||||
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
 | 
			
		||||
class Terminal {
 | 
			
		||||
public:
 | 
			
		||||
  Terminal(void);
 | 
			
		||||
  ~Terminal(void);
 | 
			
		||||
 | 
			
		||||
  void handle_input(SDL_Event* event);
 | 
			
		||||
  void render(TextRenderer* renderer, int x, int y, int width, int height, bool show_cursor);
 | 
			
		||||
@ -19,4 +21,5 @@ private:
 | 
			
		||||
  std::string _input_buffer;
 | 
			
		||||
  std::vector<std::string> _history;
 | 
			
		||||
  int _scroll_offset;
 | 
			
		||||
  ClientNetwork* _network;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								common/src/vfs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								common/src/vfs.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
struct vfs_node;
 | 
			
		||||
 | 
			
		||||
/* Store children for quick lookup by name. */
 | 
			
		||||
typedef std::map<std::string, vfs_node*> vfs_child_map;
 | 
			
		||||
 | 
			
		||||
enum vfs_node_type {
 | 
			
		||||
  FILE_NODE,
 | 
			
		||||
  DIR_NODE
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct vfs_node {
 | 
			
		||||
  std::string name;
 | 
			
		||||
  vfs_node_type type;
 | 
			
		||||
 | 
			
		||||
  /* Files. */
 | 
			
		||||
  std::string content;
 | 
			
		||||
 | 
			
		||||
  /* Directories. */
 | 
			
		||||
  vfs_child_map children;
 | 
			
		||||
  vfs_node* parent;
 | 
			
		||||
};
 | 
			
		||||
@ -4,5 +4,11 @@ add_executable(bettolas
 | 
			
		||||
  ${BETTOLAS_SOURCES}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(bettolas PRIVATE bettola)
 | 
			
		||||
find_package(SDL3 REQUIRED)
 | 
			
		||||
find_package(SDL3_net REQUIRED)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(bettolas PRIVATE bettola SDL3::SDL3 SDL3_net)
 | 
			
		||||
 | 
			
		||||
target_include_directories(bettolas PUBLIC
 | 
			
		||||
  ${CMAKE_CURRENT_SOURCE_DIR}/src
 | 
			
		||||
  /usr/local/include/SDL3_net)
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,18 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include "bettola.h"
 | 
			
		||||
 | 
			
		||||
#include "vfs_manager.h"
 | 
			
		||||
#include "network_manager.h"
 | 
			
		||||
 | 
			
		||||
int main(int argc, char** argv) {
 | 
			
		||||
  printf("=== Server starting ===\n");
 | 
			
		||||
  bettola_function();
 | 
			
		||||
 | 
			
		||||
  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());
 | 
			
		||||
 | 
			
		||||
  NetworkManager* net_manager = new NetworkManager(root_vfs);
 | 
			
		||||
  net_manager->start(); /* Our loop. */
 | 
			
		||||
 | 
			
		||||
  delete net_manager; /* Shouldn't get here. */
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										97
									
								
								server/src/network_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								server/src/network_manager.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
#include "network_manager.h"
 | 
			
		||||
 | 
			
		||||
NetworkManager::NetworkManager(vfs_node* vfs_root) {
 | 
			
		||||
  _vfs_root = vfs_root;
 | 
			
		||||
 | 
			
		||||
  if(!NET_Init()) {
 | 
			
		||||
    printf("Error: SDLNet_Init: %s\n", SDL_GetError());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  _server_socket = NET_CreateServer(NULL, 8080);
 | 
			
		||||
  if(!_server_socket) {
 | 
			
		||||
    printf("Error: NET_CreateServer: %s\n", SDL_GetError());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NetworkManager::~NetworkManager(void) {
 | 
			
		||||
  NET_DestroyServer(_server_socket);
 | 
			
		||||
  NET_Quit();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetworkManager::start(void) {
 | 
			
		||||
  printf("BettolaServer listening on port 8080...\n");
 | 
			
		||||
  while(true) {
 | 
			
		||||
    /* Check for and accept any new client connections. */
 | 
			
		||||
    _handle_new_connections();
 | 
			
		||||
 | 
			
		||||
    /* Check all existing clients for incoming data. */
 | 
			
		||||
    _handle_client_activity();
 | 
			
		||||
 | 
			
		||||
    /* Let's not be burning CPU cycles. */
 | 
			
		||||
    SDL_Delay(0);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetworkManager::_handle_new_connections(void) {
 | 
			
		||||
  NET_StreamSocket* client_socket;
 | 
			
		||||
  if(NET_AcceptClient(_server_socket, &client_socket)) {
 | 
			
		||||
    if(client_socket) {
 | 
			
		||||
      Player* new_player = new Player(client_socket);
 | 
			
		||||
      new_player->current_dir = _vfs_root;
 | 
			
		||||
      _players.push_back(new_player);
 | 
			
		||||
      printf("Client connected. Total players: %zu\n", _players.size());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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_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) {
 | 
			
		||||
      _process_command(player, buffer);
 | 
			
		||||
    } else if(bytes_received < 0 ) {
 | 
			
		||||
      _disconnect_client(player, i);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetworkManager::_process_command(Player* player, char* command) {
 | 
			
		||||
  /* We'll just do 'ls' for now. */
 | 
			
		||||
  if(strncmp(command, "ls", 2) == 0) {
 | 
			
		||||
    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 += "\n";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    /* NET_WriteToStreamSocket is essentially the new send function in SDL3. */
 | 
			
		||||
    NET_WriteToStreamSocket(player->socket, response.c_str(), response.length()+1);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void NetworkManager::_disconnect_client(Player* player, int index) {
 | 
			
		||||
  printf("Client disconnected.\n");
 | 
			
		||||
  NET_DestroyStreamSocket(player->socket);
 | 
			
		||||
  _players.erase(_players.begin() + index);
 | 
			
		||||
  delete player;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										25
									
								
								server/src/network_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								server/src/network_manager.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <SDL3/SDL.h>
 | 
			
		||||
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
#include "player.h"
 | 
			
		||||
 | 
			
		||||
class NetworkManager {
 | 
			
		||||
public:
 | 
			
		||||
  NetworkManager(vfs_node* vfs_root);
 | 
			
		||||
  ~NetworkManager(void);
 | 
			
		||||
 | 
			
		||||
  void start(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void _handle_new_connections(void);
 | 
			
		||||
  void _handle_client_activity(void);
 | 
			
		||||
  void _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;
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										7
									
								
								server/src/player.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								server/src/player.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
#include "player.h"
 | 
			
		||||
#include "SDL_net.h"
 | 
			
		||||
 | 
			
		||||
Player::Player(NET_StreamSocket* new_socket) {
 | 
			
		||||
  socket = new_socket;
 | 
			
		||||
  current_dir = nullptr; /* Will set to VFS root on connection. */
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								server/src/player.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								server/src/player.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <SDL_net.h>
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
class Player {
 | 
			
		||||
public:
 | 
			
		||||
  Player(NET_StreamSocket* socket);
 | 
			
		||||
 | 
			
		||||
  NET_StreamSocket* socket;
 | 
			
		||||
  vfs_node* current_dir;
 | 
			
		||||
private:
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										37
									
								
								server/src/vfs_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								server/src/vfs_manager.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
#include "vfs_manager.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
/* Create a new node. */
 | 
			
		||||
vfs_node* new_node(std::string name, vfs_node_type type, vfs_node* parent) {
 | 
			
		||||
  vfs_node* node = new vfs_node();
 | 
			
		||||
  node->name    = name;
 | 
			
		||||
  node->type    = type;
 | 
			
		||||
  node->parent  = parent;
 | 
			
		||||
  return node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
vfs_node* vfs_manager::create_root_system(void) {
 | 
			
		||||
  /* Rood directory. */
 | 
			
		||||
  vfs_node* root = new_node("/", DIR_NODE, nullptr);
 | 
			
		||||
 | 
			
		||||
  /* Subdirectories. */
 | 
			
		||||
  vfs_node* home = new_node("home", DIR_NODE, root);
 | 
			
		||||
  vfs_node* bin  = new_node("bin", DIR_NODE, root);
 | 
			
		||||
  root->children["home"]  = home;
 | 
			
		||||
  root->children["bin"]   = bin;
 | 
			
		||||
 | 
			
		||||
  /* User diractory. */
 | 
			
		||||
  vfs_node* user = new_node("user", DIR_NODE, home);
 | 
			
		||||
  home->children["user"] = user;
 | 
			
		||||
 | 
			
		||||
  /* Create file. */
 | 
			
		||||
  vfs_node* readme = new_node("readme.txt", FILE_NODE, user);
 | 
			
		||||
  readme->content = "Welcome to your new virtual machine.";
 | 
			
		||||
  user->children["readme.txt"] = readme;
 | 
			
		||||
 | 
			
		||||
  vfs_node* ls_exe = new_node("ls", FILE_NODE, bin);
 | 
			
		||||
  ls_exe->content = "[executable]";
 | 
			
		||||
  bin->children["ls"] = ls_exe;
 | 
			
		||||
 | 
			
		||||
  return root;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								server/src/vfs_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								server/src/vfs_manager.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
namespace vfs_manager {
 | 
			
		||||
  vfs_node* create_root_system(void);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user