[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 REQUIRED)
|
||||||
|
find_package(SDL3_net REQUIRED)
|
||||||
find_package(GLEW REQUIRED)
|
find_package(GLEW REQUIRED)
|
||||||
find_package(OpenGL REQUIRED)
|
find_package(OpenGL REQUIRED)
|
||||||
find_package(Freetype 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 <GL/glew.h>
|
||||||
#include <SDL3/SDL_events.h>
|
#include <SDL3/SDL_events.h>
|
||||||
|
|
||||||
|
#include "client_network.h"
|
||||||
#include "gfx/txt_renderer.h"
|
#include "gfx/txt_renderer.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
@ -10,6 +11,16 @@ Terminal::Terminal(void) {
|
|||||||
/* Placeholder welcome message to history. */
|
/* Placeholder welcome message to history. */
|
||||||
_history.push_back("Welcome to Bettola");
|
_history.push_back("Welcome to Bettola");
|
||||||
_scroll_offset = 0;
|
_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) {
|
void Terminal::_on_ret_press(void) {
|
||||||
@ -20,6 +31,11 @@ void Terminal::_on_ret_press(void) {
|
|||||||
* TODO: Parse and execute commands.
|
* TODO: Parse and execute commands.
|
||||||
*/
|
*/
|
||||||
printf("Command entered: %s\n", _input_buffer.c_str());
|
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") {
|
if(_input_buffer == "clear") {
|
||||||
_history.clear();
|
_history.clear();
|
||||||
|
|||||||
@ -4,10 +4,12 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "gfx/txt_renderer.h"
|
#include "gfx/txt_renderer.h"
|
||||||
|
#include "client_network.h"
|
||||||
|
|
||||||
class Terminal {
|
class Terminal {
|
||||||
public:
|
public:
|
||||||
Terminal(void);
|
Terminal(void);
|
||||||
|
~Terminal(void);
|
||||||
|
|
||||||
void handle_input(SDL_Event* event);
|
void handle_input(SDL_Event* event);
|
||||||
void render(TextRenderer* renderer, int x, int y, int width, int height, bool show_cursor);
|
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::string _input_buffer;
|
||||||
std::vector<std::string> _history;
|
std::vector<std::string> _history;
|
||||||
int _scroll_offset;
|
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}
|
${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 <cstdio>
|
||||||
#include "bettola.h"
|
|
||||||
|
#include "vfs_manager.h"
|
||||||
|
#include "network_manager.h"
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
printf("=== Server starting ===\n");
|
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;
|
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