From 97b60488de7b89bbbe6c5352bb0449eaed810245 Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Tue, 7 Oct 2025 00:05:40 +0100 Subject: [PATCH] [Add] Persistent VFS and database aware commands. --- assets/scripts/bin/ls.lua | 13 +--- common/src/command_processor.cpp | 99 +++++++++++------------------- common/src/command_processor.h | 8 ++- common/src/db/database_manager.cpp | 18 +++++- common/src/db/database_manager.h | 3 +- common/src/lua_api.cpp | 80 ++++++++++++++++++------ common/src/lua_api.h | 1 + common/src/lua_processor.cpp | 1 + common/src/machine_manager.cpp | 26 +++----- common/src/machine_manager.h | 9 ++- common/src/vfs.cpp | 9 +++ common/src/vfs.h | 2 + server/src/network_manager.cpp | 25 +++++--- server/src/network_manager.h | 2 +- server/src/player.cpp | 5 +- server/src/player.h | 4 +- 16 files changed, 174 insertions(+), 131 deletions(-) diff --git a/assets/scripts/bin/ls.lua b/assets/scripts/bin/ls.lua index 063a90b..46b18b5 100644 --- a/assets/scripts/bin/ls.lua +++ b/assets/scripts/bin/ls.lua @@ -1,14 +1,5 @@ -- /bin/ls - Lists files in a directory. -local dir = bettola.get_current_dir(context) -local output = "" - +-- -- Iterate over the 'children' map exposed via C++. -for name, node in pairs(dir.children) do - output = output .. name - if node.type == 1 then -- 1 is DIR_NODE enum from C++. - output = output .. "/" - end - output = output .. " " -end -return output +return bettola.ls(context) diff --git a/common/src/command_processor.cpp b/common/src/command_processor.cpp index 94cd732..9c786b7 100644 --- a/common/src/command_processor.cpp +++ b/common/src/command_processor.cpp @@ -9,9 +9,25 @@ #include "machine_manager.h" #include "machine.h" + +vfs_node* find_node_by_id(vfs_node* root, long long id) { + if(root->id == id) { + return root; + } + for(auto const& [name, child] : root->children) { + vfs_node* found = find_node_by_id(child, id); + if(found) { + return found; + } + } + return nullptr; +} + CommandProcessor::CommandProcessor(Machine* home_machine, std::map& world_machines, - DatabaseManager* db_manager) : + DatabaseManager* db_manager, + MachineManager* machine_manager) : + _machine_manager(machine_manager), _db_manager(db_manager), _home_machine(home_machine), _session_machine(home_machine), @@ -36,6 +52,14 @@ CommandProcessor::get_world_machines(void) { return _world_machines; } +DatabaseManager* CommandProcessor::get_db_manager(void) { + return _db_manager; +} + +MachineManager* CommandProcessor::get_machine_manager(void) { + return _machine_manager; +} + void CommandProcessor::set_current_dir(vfs_node* node) { _current_dir = node; } @@ -67,13 +91,18 @@ std::string CommandProcessor::process_command(const std::string& command) { /* Search for script in the /bin directory of the current session machine. */ std::string script_filename = command_name + ".lua"; - vfs_node* root = _session_machine->vfs_root; - if(root->children.count("bin") && root->children["bin"]->children.count(script_filename)) { - vfs_node* script_node = root->children["bin"]->children[script_filename]; - bool is_remote = (_session_machine != _home_machine); - sol::object result = _lua->execute(script_node->content, *this, args, is_remote); - return result.is() ? result.as() : "[Script returned an unexpected type]"; + long long script_id = 0; + std::string script_content; + _db_manager->_db << "SELECT T2.id, T2.content FROM vfs_nodes AS T1 JOIN vfs_nodes AS T2 " + "ON T1.id = T2.parent_id WHERE T1.name = 'bin' AND T2.name = ? " + "AND t1.machine_id = ?;" + << script_filename << _session_machine->id + >> std::tie(script_id, script_content); + if(script_id > 0) { + bool is_remote = (_session_machine != _home_machine); + sol::object result = _lua->execute(script_content, *this, args, is_remote); + return result.is() ? result.as() : "[Script returned an unexpected type]"; } return "Unknown command: " + command_name + "\n"; } @@ -114,59 +143,3 @@ std::string CommandProcessor::read_file(const std::string& path) { } return "Error: file not found."; } - -void CommandProcessor::ensure_vfs_is_writable(void) { - if(!_session_machine->is_vfs_a_copy) { - /* VFS shared, copy required. */ - if(_session_machine == _home_machine) { - /* We are modifying our own home machine. */ - fprintf(stderr, "CoW: Write attempt on player's home machine." - "Creating persistent copy.\n"); - std::string original_path = get_full_path(_current_dir); - - vfs_node* new_vfs_root = copy_vfs_node(_home_machine->vfs_root, nullptr); - auto* new_machine = new Machine(_home_machine->id, _home_machine->hostname); - new_machine->vfs_root = new_vfs_root; - new_machine->services = _home_machine->services; - new_machine->is_vfs_a_copy = true; - - Machine* old_machine = _home_machine; - _home_machine = new_machine; - set_session_machine(new_machine); - delete old_machine; - _current_dir = find_node_by_path(_session_machine->vfs_root, original_path); - if(!_current_dir) { _current_dir = _session_machine->vfs_root; } - } else { - /* we are modifying a remote NPC machine. */ - std::string remote_ip = ""; - for(auto const& [ip, machine] : _world_machines) { - if(machine == _session_machine) { - remote_ip = ip; - break; - } - } - if(!remote_ip.empty()) { - fprintf(stderr, "CoW: Write attempt on remote machine '%s'." - "Creating persistent copy.\n", - remote_ip.c_str()); - - std::string original_path = get_full_path(_current_dir); - - vfs_node* new_vfs_root = copy_vfs_node(_session_machine->vfs_root, nullptr); - auto* new_machine = new Machine(_session_machine->id, _session_machine->hostname); - new_machine->vfs_root = new_vfs_root; - new_machine->services = _session_machine->services; - new_machine->is_vfs_a_copy = true; - - Machine* old_machine = _world_machines[remote_ip]; - _world_machines[remote_ip] = new_machine; - set_session_machine(new_machine); - delete old_machine; - - _current_dir = find_node_by_path(_session_machine->vfs_root, original_path); - if(!_current_dir) { _current_dir = _session_machine->vfs_root; } - - } - } - } -} diff --git a/common/src/command_processor.h b/common/src/command_processor.h index 5fa0cc4..87f2d30 100644 --- a/common/src/command_processor.h +++ b/common/src/command_processor.h @@ -6,12 +6,13 @@ #include "db/database_manager.h" #include "machine.h" +#include "machine_manager.h" #include "vfs.h" class CommandProcessor { public: CommandProcessor(Machine* home_machine, std::map& world_machines, - DatabaseManager* db_manager); + DatabaseManager* db_manager, MachineManager* machine_manager); ~CommandProcessor(void); std::string process_command(const std::string& command); @@ -23,13 +24,14 @@ public: Machine* get_home_machine(void); Machine* get_session_machine(void); std::map& get_world_machines(void); + DatabaseManager* get_db_manager(void); + MachineManager* get_machine_manager(void); void set_current_dir(vfs_node* node); void set_session_machine(Machine* machine); - void ensure_vfs_is_writable(void); - private: + MachineManager* _machine_manager; DatabaseManager* _db_manager; Machine* _home_machine; Machine* _session_machine; diff --git a/common/src/db/database_manager.cpp b/common/src/db/database_manager.cpp index 1629379..60b3d4f 100644 --- a/common/src/db/database_manager.cpp +++ b/common/src/db/database_manager.cpp @@ -1,4 +1,5 @@ #include "database_manager.h" +#include "vfs.h" DatabaseManager::DatabaseManager(const std::string& db_path) : _db(db_path) { /* db is opened in the construtor's init list. */ @@ -35,7 +36,7 @@ void DatabaseManager::init(void) { } bool DatabaseManager::create_player(const std::string& username, const std::string& password, - const std::string& hostname) { + const std::string& hostname, vfs_node* vfs_template) { long long player_id = 0; long long machine_id = 0; @@ -66,13 +67,24 @@ bool DatabaseManager::create_player(const std::string& username, const std::stri 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"; + /* Create /bing and get it's ID */ + _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type) VALUES(?, ?, ?, 1);" + << machine_id << root_id << "bin"; + long long bin_id = _db.last_insert_rowid(); + + /* Copy scripts from template into new machine's /bin */ + vfs_node* template_bin = vfs_template->children["bin"]; + for(auto const& [name, node] : template_bin->children) { + _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type, content) " + "VALUES (?, ?, ?, ?, ?);" + << machine_id << bin_id << name << FILE_NODE <content; + } + _db << "COMMIT"; } catch(const std::exception& e) { _db << "ROLLBACK;"; /* Ensure atomicity. */ diff --git a/common/src/db/database_manager.h b/common/src/db/database_manager.h index 609f5d6..9961438 100644 --- a/common/src/db/database_manager.h +++ b/common/src/db/database_manager.h @@ -2,6 +2,7 @@ #include +#include "vfs.h" #include "db.h" class DatabaseManager { @@ -13,7 +14,7 @@ public: /* Return true on success, false if user already exists. */ bool create_player(const std::string& username, const std::string& password, - const std::string& hostname); + const std::string& hostname, vfs_node* vfs_template); /* Return true if creds are valid. */ bool auth_player(const std::string& username, const std::string& password); diff --git a/common/src/lua_api.cpp b/common/src/lua_api.cpp index 525cd08..57cad1d 100644 --- a/common/src/lua_api.cpp +++ b/common/src/lua_api.cpp @@ -1,7 +1,9 @@ +#include #include #include "lua_api.h" #include "command_processor.h" +#include "db/database_manager.h" #include "machine.h" #include "vfs.h" @@ -12,33 +14,47 @@ vfs_node* get_current_dir(CommandProcessor& context) { } std::string rm(CommandProcessor& context, const std::string& filename) { - context.ensure_vfs_is_writable(); vfs_node* current_dir = context.get_current_dir(); - if(!current_dir->children.count(filename)) { + DatabaseManager* db = context.get_db_manager(); + + /* find the file in the database. */ + long long file_id = 0; + int file_type = 0; + db->_db << "SELECT id, type FROM vfs_nodes WHERE parent_id = ? AND name = ?;" + << current_dir->id << filename + >> std::tie(file_id, file_type); + + if(file_id == 0) { return "rm: cannot remove '" + filename + "': No such file or directory."; } - vfs_node* target_node = current_dir->children[filename]; - if(target_node->type == DIR_NODE) { + if(file_type == DIR_NODE) { return "rm: cannot remove '" + filename + "': Is a directory."; } + + db->_db << "DELETE FROM vfs_nodes WHERE id = ?;" << file_id; + /* Also remove from in-mem VFS. */ + delete current_dir->children[filename]; current_dir->children.erase(filename); - delete target_node; + return ""; } std::string write_file(CommandProcessor& context, const std::string& filename, const std::string& content) { - context.ensure_vfs_is_writable(); vfs_node* current_dir = context.get_current_dir(); + DatabaseManager* db = context.get_db_manager(); + if(current_dir->children.count(filename)) { vfs_node* target_node = current_dir->children[filename]; - if(target_node->type == DIR_NODE) { - return "cannot write to '" + filename + "': Is a directory."; - } + db->_db << "UPDATE vfs_nodes SET content = ? WHERE id = ?;" + << content << target_node->id; target_node->content = content; } else { - vfs_node* new_file = new vfs_node { - .name = filename, .type = FILE_NODE, .content = content, .parent = current_dir}; + db->_db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type, content) VALUES (?, ?, ?, ?, ?);" + << context.get_session_machine()->id << current_dir->id << filename << FILE_NODE << content; + vfs_node* new_file = new_node(filename, FILE_NODE, current_dir); + new_file->id = db->_db.last_insert_rowid(); + new_file->content = content; current_dir->children[filename] = new_file; } return ""; @@ -46,23 +62,51 @@ std::string write_file(CommandProcessor& context, const std::string& filename, std::string cd(CommandProcessor& context, const std::string& path) { vfs_node* current_dir = context.get_current_dir(); + DatabaseManager* db = context.get_db_manager(); + if(path == "..") { if(current_dir->parent) { context.set_current_dir(current_dir->parent); } - } else if(current_dir->children.count(path)) { - vfs_node* target_node = current_dir->children[path]; - if(target_node->type == DIR_NODE) { - context.set_current_dir(target_node); - } else { - return "cd: not a directory: " + path; - } } else { + long long target_id = 0; + db->_db << "SELECT id FROM vfs_nodes WHERE parent_id = ? AND name = ? AND type = 1;" + << current_dir->id << path + >> target_id; + + if(target_id > 0) { + /* Inefficient, I know. */ + Machine* reloaded_machine = + context.get_machine_manager()->load_machine(context.get_session_machine()->id, db); + vfs_node* new_dir = find_node_by_id(reloaded_machine->vfs_root, target_id); + context.set_current_dir(new_dir); + } else { return "cd: no such file or directory: " + path; + } } return ""; } +std::string ls(CommandProcessor& context) { + vfs_node* dir = context.get_current_dir(); + printf("DEBUG: ls command called for directory_id: %lld\n", dir->id); + if(dir->type != DIR_NODE) { + return "ls: not a directory"; + } + + std::stringstream ss; + + context.get_db_manager()->_db << "SELECT name, type FROM vfs_nodes WHERE parent_id = ?;" + << dir->id + >> [&](std::string name, int type) { + ss << name; + if(type == DIR_NODE) ss << "/"; + ss << " "; + }; + + return ss.str(); +} + std::string ssh(CommandProcessor& context, const std::string& ip) { if(context.get_world_machines().count(ip)) { context.set_session_machine(context.get_world_machines().at(ip)); diff --git a/common/src/lua_api.h b/common/src/lua_api.h index 4c7b038..ed16240 100644 --- a/common/src/lua_api.h +++ b/common/src/lua_api.h @@ -12,6 +12,7 @@ vfs_node* get_current_dir(CommandProcessor& context); std::string rm(CommandProcessor& context, const std::string& filename); std::string write_file(CommandProcessor& context, const std::string& filename, const std::string& content); +std::string ls(CommandProcessor& context); std::string cd(CommandProcessor& context, const std::string& path); /* NETWORK ACTIONS. */ diff --git a/common/src/lua_processor.cpp b/common/src/lua_processor.cpp index 2bd05ea..08d0f3e 100644 --- a/common/src/lua_processor.cpp +++ b/common/src/lua_processor.cpp @@ -24,6 +24,7 @@ LuaProcessor::LuaProcessor(void) { /* Create the 'bettola' API table. */ sol::table bettola_api = _lua.create_named_table("bettola"); bettola_api["rm"] = &api::rm; + bettola_api["ls"] = &api::ls; bettola_api["write_file"] = &api::write_file; bettola_api["get_current_dir"]= &api::get_current_dir; bettola_api["cd"] = &api::cd; diff --git a/common/src/machine_manager.cpp b/common/src/machine_manager.cpp index 9f828f9..367f3b9 100644 --- a/common/src/machine_manager.cpp +++ b/common/src/machine_manager.cpp @@ -8,15 +8,6 @@ #include "machine.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* copy_vfs_node(vfs_node* original, vfs_node* new_parent) { if(!original) { return nullptr; @@ -34,7 +25,8 @@ vfs_node* copy_vfs_node(vfs_node* original, vfs_node* new_parent) { return new_copy; } -MachineManager::MachineManager(void) { +MachineManager::MachineManager(DatabaseManager* db_manager) : + _db_manager(db_manager) { /* Create template VFS that holds shared, read-only directories. */ _vfs_template_root = new_node("/", DIR_NODE, nullptr); vfs_node* bin = new_node("bin", DIR_NODE, _vfs_template_root); @@ -107,12 +99,11 @@ void build_tree(vfs_node* parent, const std::map& nodes) { } } -Machine* MachineManager::load_machine(long long machine_id) { - DatabaseManager db("bettola.db"); /* Assumes multiplayer for now. */ - +Machine* MachineManager::load_machine(long long machine_id, DatabaseManager* db_manager) { + printf("DEBUG: load_machine called for machine_id: %lld\n", machine_id); std::string hostname; - db._db << "SELECT hostname FROM machines WHERE id = ?;" + db_manager->_db << "SELECT hostname FROM machines WHERE id = ?;" << machine_id >> hostname; @@ -122,12 +113,14 @@ Machine* MachineManager::load_machine(long long machine_id) { std::map nodes; vfs_node* root = nullptr; - db._db << "SELECT id, parent_id, name, type, content FROM vfs_nodes WHERE machine_id = ?;" + db_manager->_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); + vfs_node* node = new vfs_node(); node->id = id; node->parent_id = parent_id; /* Store temp id for tree building. */ + node->name = name; + node->type = (vfs_node_type)type; node->content = content; nodes[id] = node; if(name == "/") { @@ -140,5 +133,4 @@ Machine* MachineManager::load_machine(long long machine_id) { machine->vfs_root = root; } return machine; - } diff --git a/common/src/machine_manager.h b/common/src/machine_manager.h index b29d268..39a878c 100644 --- a/common/src/machine_manager.h +++ b/common/src/machine_manager.h @@ -2,6 +2,7 @@ #include +#include "db/database_manager.h" #include "machine.h" #include "vfs.h" @@ -10,13 +11,17 @@ vfs_node* copy_vfs_node(vfs_node* original, vfs_node* new_parent); class MachineManager { public: - MachineManager(void); + MachineManager(DatabaseManager* db_manager); ~MachineManager(void); /* TODO: Implement recursive VFS deletion. */ Machine* create_machine(uint32_t id, const std::string& hostname, const std::string& system_type); - Machine* load_machine(long long machine_id); + Machine* load_machine(long long machine_id, DatabaseManager* db_manager); + + vfs_node* get_vfs_template(void) { return _vfs_template_root; } + private: + DatabaseManager* _db_manager; vfs_node* _vfs_template_root; }; diff --git a/common/src/vfs.cpp b/common/src/vfs.cpp index 6955512..b8724f8 100644 --- a/common/src/vfs.cpp +++ b/common/src/vfs.cpp @@ -1,5 +1,14 @@ #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; +} + std::string get_full_path(vfs_node* node) { if(node->parent == nullptr) { return "/"; diff --git a/common/src/vfs.h b/common/src/vfs.h index 77ee016..e601d38 100644 --- a/common/src/vfs.h +++ b/common/src/vfs.h @@ -27,5 +27,7 @@ struct vfs_node { vfs_node* parent; }; +vfs_node* new_node(std::string name, vfs_node_type type, vfs_node* parent); +vfs_node* find_node_by_id(vfs_node* root, long long id); std::string get_full_path(vfs_node* node); void delete_vfs_tree(vfs_node* node); diff --git a/server/src/network_manager.cpp b/server/src/network_manager.cpp index 9abd38a..4a7b79f 100644 --- a/server/src/network_manager.cpp +++ b/server/src/network_manager.cpp @@ -16,7 +16,8 @@ NetworkManager::NetworkManager(const std::string& db_path) : _db_manager(std::make_unique(db_path)), - _acceptor(_io_context) { + _acceptor(_io_context), + _machine_manager(_db_manager.get()) { /* World setup. */ _world_machines["8.8.8.8"] = _machine_manager.create_machine(1000, "dns.google", "npc"); _world_machines["10.0.2.15"] = _machine_manager.create_machine(1001, "corp.internal", "npc"); @@ -78,7 +79,8 @@ void NetworkManager::start_accept(void) { Machine* player_machine = _machine_manager.create_machine(player_id, "player.home", "player"); auto new_player = std::make_unique(player_id, player_machine, - _world_machines, _db_manager.get()); + _world_machines, _db_manager.get(), + &_machine_manager); Player* new_player_ptr = new_player.get(); _players[player_id] = std::move(new_player); new_connection->set_id(player_id); @@ -129,12 +131,14 @@ void NetworkManager::on_message(std::shared_ptr connection, std::string payload = message.substr(7); auto parts = split_message(payload, "::"); if(parts.size() == 3) { - if(_db_manager->create_player(parts[0], parts[1], parts[2])) { + if(_db_manager->create_player(parts[0], parts[1], parts[2], + _machine_manager.get_vfs_template())) { long long machine_id = _db_manager->get_player_home_machine_id(parts[0]); - Machine* home_machine = _machine_manager.load_machine(machine_id); + Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get()); delete player->cmd_processor; /* Delete old command processor. */ player->cmd_processor = new CommandProcessor(home_machine, _world_machines, - _db_manager.get()); + _db_manager.get(), + &_machine_manager); player->state = PlayerState::ACTIVE; connection->send("C_ACC_SUCCESS"); /* send initial prompt. */ @@ -149,10 +153,13 @@ void NetworkManager::on_message(std::shared_ptr connection, auto parts = split_message(payload, "::"); if(parts.size() == 2) { if(_db_manager->auth_player(parts[0], parts[1])) { - /* - * TODO: Load the player's machine from the database. - * For now, just auth them and use a temp machine. - */ + long long machine_id = _db_manager->get_player_home_machine_id(parts[0]); + printf("DEBUG: Loading machine %lld for player %s\n", machine_id, parts[0].c_str()); + Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get()); + delete player->cmd_processor; /* Delete old command processor. */ + player->cmd_processor = new CommandProcessor(home_machine, _world_machines, + _db_manager.get(), &_machine_manager); + player->state = PlayerState::ACTIVE; connection->send("LOGIN_SUCCESS"); /* Send initial prompt. */ diff --git a/server/src/network_manager.h b/server/src/network_manager.h index b0d0c57..dcf2f90 100644 --- a/server/src/network_manager.h +++ b/server/src/network_manager.h @@ -37,6 +37,6 @@ private: uint32_t _next_player_id = 1; std::map _world_machines; /* For NPC's. */ - MachineManager _machine_manager; std::unique_ptr _db_manager; + MachineManager _machine_manager; }; diff --git a/server/src/player.cpp b/server/src/player.cpp index db69ecc..a6e4514 100644 --- a/server/src/player.cpp +++ b/server/src/player.cpp @@ -4,11 +4,12 @@ #include "machine.h" Player::Player(uint32_t new_id, Machine* home_machine, - std::map& world_machines, DatabaseManager* db_manager) : + std::map& world_machines, DatabaseManager* db_manager, + MachineManager* machine_manager) : id(new_id), state(PlayerState::AUTHENTICATING) { - cmd_processor = new CommandProcessor(home_machine, world_machines, db_manager); + cmd_processor = new CommandProcessor(home_machine, world_machines, db_manager, machine_manager); } Player::~Player(void) { diff --git a/server/src/player.h b/server/src/player.h index a960a5a..7215b95 100644 --- a/server/src/player.h +++ b/server/src/player.h @@ -7,6 +7,7 @@ #include "db/database_manager.h" #include "command_processor.h" #include "machine.h" +#include "machine_manager.h" enum class PlayerState { AUTHENTICATING, @@ -16,7 +17,8 @@ enum class PlayerState { class Player { public: Player(uint32_t id, Machine* home_machine, std::map& world_machines, DatabaseManager* db_manager); + Machine*>& world_machines, DatabaseManager* db_manager, + MachineManager* machine_manager); ~Player(void); uint32_t id;