From 44ca427c0e8b5ff1f726ac18cc6326f6ca65f0f6 Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Mon, 6 Oct 2025 20:41:22 +0100 Subject: [PATCH] [Add] Completed the persistance layer. --- common/src/command_processor.cpp | 5 ++- common/src/command_processor.h | 5 ++- common/src/db/database_manager.cpp | 62 +++++++++++++++++++++++++++++- common/src/db/database_manager.h | 3 +- common/src/machine_manager.cpp | 52 +++++++++++++++++++++++++ common/src/machine_manager.h | 1 + common/src/vfs.h | 2 + server/src/network_manager.cpp | 13 ++++--- server/src/player.cpp | 5 ++- server/src/player.h | 4 +- 10 files changed, 138 insertions(+), 14 deletions(-) diff --git a/common/src/command_processor.cpp b/common/src/command_processor.cpp index b09746a..94cd732 100644 --- a/common/src/command_processor.cpp +++ b/common/src/command_processor.cpp @@ -2,6 +2,7 @@ #include #include "command_processor.h" +#include "db/database_manager.h" #include "vfs.h" #include "lua_api.h" #include "lua_processor.h" @@ -9,7 +10,9 @@ #include "machine.h" CommandProcessor::CommandProcessor(Machine* home_machine, - std::map& world_machines) : + std::map& world_machines, + DatabaseManager* db_manager) : + _db_manager(db_manager), _home_machine(home_machine), _session_machine(home_machine), _world_machines(world_machines) { diff --git a/common/src/command_processor.h b/common/src/command_processor.h index 5b3be66..5fa0cc4 100644 --- a/common/src/command_processor.h +++ b/common/src/command_processor.h @@ -4,12 +4,14 @@ #include #include +#include "db/database_manager.h" #include "machine.h" #include "vfs.h" class CommandProcessor { public: - CommandProcessor(Machine* home_machine, std::map& world_machines); + CommandProcessor(Machine* home_machine, std::map& world_machines, + DatabaseManager* db_manager); ~CommandProcessor(void); std::string process_command(const std::string& command); @@ -28,6 +30,7 @@ public: void ensure_vfs_is_writable(void); private: + DatabaseManager* _db_manager; Machine* _home_machine; Machine* _session_machine; vfs_node* _current_dir; diff --git a/common/src/db/database_manager.cpp b/common/src/db/database_manager.cpp index 7149e64..1629379 100644 --- a/common/src/db/database_manager.cpp +++ b/common/src/db/database_manager.cpp @@ -13,19 +13,69 @@ void DatabaseManager::init(void) { "id INTEGER PRIMARY KEY AUTOINCREMENT," "username TEXT NOT NULL UNIQUE," "password TEXT NOT NULL," - "hostname TEXT NOT NULL" + "hostname TEXT NOT NULL," + "home_machine_id INTEGER" + ");"; + + _db << "CREATE TABLE IF NOT EXISTS machines (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "owner_id INTEGER," + "hostname TEXT NOT NULL," + "ip_address TEXT NOT NULL UNIQUE" + ");"; + + _db << "CREATE TABLE IF NOT EXISTS vfs_nodes (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + "machine_id INTEGER NOT NULL," + "parent_id INTEGER," + "name TEXT NOT NULL," + "type INTEGER NOT NULL," + "content TEXT" ");"; } bool DatabaseManager::create_player(const std::string& username, const std::string& password, const std::string& hostname) { + long long player_id = 0; + long long machine_id = 0; + try { + _db << "BEGIN;"; + + /* Create the player. */ _db << "INSERT INTO players (username, password, hostname) VALUES (?, ?, ?);" << username << password << hostname; + player_id = _db.last_insert_rowid(); + + /* Create the home machine. */ + /* TODO: Implement real IP allication. */ + std::string ip_address = "192.168.1." + std::to_string(player_id); + _db << "INSERT INTO machines (owner_id, hostname, ip_address) VALUES (?, ?, ?);" + << player_id << hostname << ip_address; + machine_id = _db.last_insert_rowid(); + + /* Link player to their new machine. */ + _db << "UPDATE players SET home_machine_id = ? WHERE id = ?;" + << machine_id << player_id; + + /* Create the root dir for the new machine's VFS. */ + _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type) VALUES(?, NULL, ?, 1);" + << machine_id << "/"; + long long root_id = _db.last_insert_rowid(); + + /* Create default subdirs. */ + _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type) VALUES (?, ?, ?, 1);" + << machine_id << root_id << "bin"; + _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type) VALUES (?, ?, ?, 1);" + << machine_id << root_id << "home"; + _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type) VALUES (?, ?, ?, 1);" + << machine_id << root_id << "etc"; + + _db << "COMMIT"; } catch(const std::exception& e) { - /* Fail if the username exists. */ + _db << "ROLLBACK;"; /* Ensure atomicity. */ return false; } return true; @@ -40,3 +90,11 @@ bool DatabaseManager::auth_player(const std::string& username, const std::string return authed; } + +long long DatabaseManager::get_player_home_machine_id(const std::string& username) { + long long machine_id = -1; /* Return -1 if not found. */ + _db << "SELECT home_machine_id FROM players WHERE username = ?;" + << username + >> machine_id; + return machine_id; +} diff --git a/common/src/db/database_manager.h b/common/src/db/database_manager.h index bc75611..609f5d6 100644 --- a/common/src/db/database_manager.h +++ b/common/src/db/database_manager.h @@ -18,6 +18,7 @@ public: /* Return true if creds are valid. */ bool auth_player(const std::string& username, const std::string& password); -private: + long long get_player_home_machine_id(const std::string& username); + sqlite::database _db; }; diff --git a/common/src/machine_manager.cpp b/common/src/machine_manager.cpp index 224b04f..9f828f9 100644 --- a/common/src/machine_manager.cpp +++ b/common/src/machine_manager.cpp @@ -1,7 +1,9 @@ #include #include #include +#include +#include "db/database_manager.h" #include "machine_manager.h" #include "machine.h" #include "vfs.h" @@ -90,3 +92,53 @@ Machine* MachineManager::create_machine(uint32_t id, const std::string& hostname return new_machine; } + +/* Recursively build the VFS tree from database nodes. */ +void build_tree(vfs_node* parent, const std::map& nodes) { + for(auto const& [id, node] : nodes) { + /* Inefficient but safe. Would be better to group nodes by parent_id. */ + if(node->parent_id == parent->id) { + parent->children[node->name] = node; + node->parent = parent; + if(node->type == DIR_NODE) { + build_tree(node, nodes); + } + } + } +} + +Machine* MachineManager::load_machine(long long machine_id) { + DatabaseManager db("bettola.db"); /* Assumes multiplayer for now. */ + + std::string hostname; + + db._db << "SELECT hostname FROM machines WHERE id = ?;" + << machine_id + >> hostname; + + Machine* machine = new Machine(machine_id, hostname); + + /* Load all VFS nodes for this machine from the database. */ + std::map nodes; + vfs_node* root = nullptr; + + db._db << "SELECT id, parent_id, name, type, content FROM vfs_nodes WHERE machine_id = ?;" + << machine_id + >> [&](long long id, long long parent_id, std::string name, int type, std::string content) { + vfs_node* node = new_node(name, (vfs_node_type)type, nullptr); + node->id = id; + node->parent_id = parent_id; /* Store temp id for tree building. */ + node->content = content; + nodes[id] = node; + if(name == "/") { + root = node; + } + }; + + if(root) { + build_tree(root, nodes); + machine->vfs_root = root; + } + return machine; + +} diff --git a/common/src/machine_manager.h b/common/src/machine_manager.h index 2db9d84..b29d268 100644 --- a/common/src/machine_manager.h +++ b/common/src/machine_manager.h @@ -15,6 +15,7 @@ public: Machine* create_machine(uint32_t id, const std::string& hostname, const std::string& system_type); + Machine* load_machine(long long machine_id); private: vfs_node* _vfs_template_root; }; diff --git a/common/src/vfs.h b/common/src/vfs.h index 82865cb..77ee016 100644 --- a/common/src/vfs.h +++ b/common/src/vfs.h @@ -14,6 +14,8 @@ enum vfs_node_type { }; struct vfs_node { + long long id; + long long parent_id; /* Used durin DB loading only. */ std::string name; vfs_node_type type; diff --git a/server/src/network_manager.cpp b/server/src/network_manager.cpp index ccc1009..9abd38a 100644 --- a/server/src/network_manager.cpp +++ b/server/src/network_manager.cpp @@ -2,7 +2,6 @@ #include #include #include -#include #include "network_manager.h" #include "db/database_manager.h" @@ -78,7 +77,8 @@ void NetworkManager::start_accept(void) { uint32_t player_id = _next_player_id++; Machine* player_machine = _machine_manager.create_machine(player_id, "player.home", "player"); - auto new_player = std::make_unique(player_id, player_machine, _world_machines); + auto new_player = std::make_unique(player_id, player_machine, + _world_machines, _db_manager.get()); Player* new_player_ptr = new_player.get(); _players[player_id] = std::move(new_player); new_connection->set_id(player_id); @@ -130,10 +130,11 @@ void NetworkManager::on_message(std::shared_ptr connection, auto parts = split_message(payload, "::"); if(parts.size() == 3) { if(_db_manager->create_player(parts[0], parts[1], parts[2])) { - /* - * TODO: When creating a player, also create their machine and save it. - * for now, they will juse use a temp machine on auth. - */ + long long machine_id = _db_manager->get_player_home_machine_id(parts[0]); + Machine* home_machine = _machine_manager.load_machine(machine_id); + delete player->cmd_processor; /* Delete old command processor. */ + player->cmd_processor = new CommandProcessor(home_machine, _world_machines, + _db_manager.get()); player->state = PlayerState::ACTIVE; connection->send("C_ACC_SUCCESS"); /* send initial prompt. */ diff --git a/server/src/player.cpp b/server/src/player.cpp index 6a909a5..db69ecc 100644 --- a/server/src/player.cpp +++ b/server/src/player.cpp @@ -1,13 +1,14 @@ #include "player.h" #include "command_processor.h" +#include "db/database_manager.h" #include "machine.h" Player::Player(uint32_t new_id, Machine* home_machine, - std::map& world_machines) : + std::map& world_machines, DatabaseManager* db_manager) : id(new_id), state(PlayerState::AUTHENTICATING) { - cmd_processor = new CommandProcessor(home_machine, world_machines); + cmd_processor = new CommandProcessor(home_machine, world_machines, db_manager); } Player::~Player(void) { diff --git a/server/src/player.h b/server/src/player.h index 5320281..a960a5a 100644 --- a/server/src/player.h +++ b/server/src/player.h @@ -4,6 +4,7 @@ #include #include +#include "db/database_manager.h" #include "command_processor.h" #include "machine.h" @@ -14,7 +15,8 @@ enum class PlayerState { class Player { public: - Player(uint32_t id, Machine* home_machine, std::map& world_machines); + Player(uint32_t id, Machine* home_machine, std::map& world_machines, DatabaseManager* db_manager); ~Player(void); uint32_t id;