[Add] Completed the persistance layer.
This commit is contained in:
		
							parent
							
								
									fce3b3aad6
								
							
						
					
					
						commit
						44ca427c0e
					
				@ -2,6 +2,7 @@
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#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<std::string, Machine*>& world_machines) :
 | 
			
		||||
                                   std::map<std::string, Machine*>& world_machines,
 | 
			
		||||
                                   DatabaseManager* db_manager) :
 | 
			
		||||
    _db_manager(db_manager),
 | 
			
		||||
    _home_machine(home_machine),
 | 
			
		||||
    _session_machine(home_machine),
 | 
			
		||||
    _world_machines(world_machines) {
 | 
			
		||||
 | 
			
		||||
@ -4,12 +4,14 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include "db/database_manager.h"
 | 
			
		||||
#include "machine.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
class CommandProcessor {
 | 
			
		||||
public:
 | 
			
		||||
  CommandProcessor(Machine* home_machine, std::map<std::string, Machine*>& world_machines);
 | 
			
		||||
  CommandProcessor(Machine* home_machine, std::map<std::string, Machine*>& 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;
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,9 @@
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#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<long long, vfs_node*>& 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<long long, vfs_node*> 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;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,6 @@
 | 
			
		||||
#include <exception>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#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>(player_id, player_machine, _world_machines);
 | 
			
		||||
        auto new_player = std::make_unique<Player>(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<net::TcpConnection> 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. */
 | 
			
		||||
 | 
			
		||||
@ -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<std::string, Machine*>& world_machines) :
 | 
			
		||||
               std::map<std::string, Machine*>& 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) {
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#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<std::string, Machine*>& world_machines);
 | 
			
		||||
  Player(uint32_t id, Machine* home_machine, std::map<std::string,
 | 
			
		||||
         Machine*>& world_machines, DatabaseManager* db_manager);
 | 
			
		||||
  ~Player(void);
 | 
			
		||||
 | 
			
		||||
  uint32_t id;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user