[Refactor] Implement in-memory VFS cache.
Refactors VFS to use a cetralised, in memory caching on the server. This resolves performance and state sync issues from the previous implementation.
This commit is contained in:
parent
9d770ef9b2
commit
4f5436f376
@ -3,10 +3,9 @@
|
|||||||
|
|
||||||
#include "command_processor.h"
|
#include "command_processor.h"
|
||||||
#include "db/database_manager.h"
|
#include "db/database_manager.h"
|
||||||
#include "vfs.h"
|
#include "i_network_bridge.h"
|
||||||
#include "lua_api.h"
|
#include "lua_api.h"
|
||||||
#include "lua_processor.h"
|
#include "lua_processor.h"
|
||||||
#include "machine_manager.h"
|
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
|
|
||||||
|
|
||||||
@ -24,20 +23,19 @@ vfs_node* find_node_by_id(vfs_node* root, long long id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CommandProcessor::CommandProcessor(Machine* home_machine,
|
CommandProcessor::CommandProcessor(Machine* home_machine,
|
||||||
std::map<std::string, Machine*>& world_machines,
|
|
||||||
DatabaseManager* db_manager,
|
DatabaseManager* db_manager,
|
||||||
MachineManager* machine_manager) :
|
MachineManager* machine_manager,
|
||||||
|
INetworkBridge* network_bridge) :
|
||||||
_machine_manager(machine_manager),
|
_machine_manager(machine_manager),
|
||||||
_db_manager(db_manager),
|
_db_manager(db_manager),
|
||||||
|
_network_bridge(network_bridge),
|
||||||
_home_machine(home_machine),
|
_home_machine(home_machine),
|
||||||
_session_machine(home_machine),
|
_session_machine(home_machine) {
|
||||||
_world_machines(world_machines) {
|
|
||||||
_lua = new LuaProcessor();
|
if(home_machine) _current_dir = home_machine->vfs_root;
|
||||||
_current_dir = _session_machine->vfs_root;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandProcessor::~CommandProcessor(void) {
|
CommandProcessor::~CommandProcessor(void) {
|
||||||
delete _lua;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vfs_node* CommandProcessor::get_current_dir(void) {
|
vfs_node* CommandProcessor::get_current_dir(void) {
|
||||||
@ -47,11 +45,6 @@ vfs_node* CommandProcessor::get_current_dir(void) {
|
|||||||
Machine* CommandProcessor::get_home_machine(void) { return _home_machine; }
|
Machine* CommandProcessor::get_home_machine(void) { return _home_machine; }
|
||||||
Machine* CommandProcessor::get_session_machine(void) { return _session_machine; }
|
Machine* CommandProcessor::get_session_machine(void) { return _session_machine; }
|
||||||
|
|
||||||
std::map<std::string, Machine*>&
|
|
||||||
CommandProcessor::get_world_machines(void) {
|
|
||||||
return _world_machines;
|
|
||||||
}
|
|
||||||
|
|
||||||
DatabaseManager* CommandProcessor::get_db_manager(void) {
|
DatabaseManager* CommandProcessor::get_db_manager(void) {
|
||||||
return _db_manager;
|
return _db_manager;
|
||||||
}
|
}
|
||||||
@ -60,6 +53,10 @@ MachineManager* CommandProcessor::get_machine_manager(void) {
|
|||||||
return _machine_manager;
|
return _machine_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INetworkBridge* CommandProcessor::get_network_bridge(void) {
|
||||||
|
return _network_bridge;
|
||||||
|
}
|
||||||
|
|
||||||
void CommandProcessor::set_current_dir(vfs_node* node) {
|
void CommandProcessor::set_current_dir(vfs_node* node) {
|
||||||
_current_dir = node;
|
_current_dir = node;
|
||||||
}
|
}
|
||||||
@ -72,6 +69,12 @@ void CommandProcessor::set_session_machine(Machine* machine) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string CommandProcessor::process_command(const std::string& command) {
|
std::string CommandProcessor::process_command(const std::string& command) {
|
||||||
|
/*
|
||||||
|
* Creating the Lua processor on-demand to ensure it exists on the same
|
||||||
|
* thread that will execute the script.
|
||||||
|
*/
|
||||||
|
LuaProcessor lua(*this);
|
||||||
|
|
||||||
/* Trim trailing whitespace. */
|
/* Trim trailing whitespace. */
|
||||||
std::string cmd = command;
|
std::string cmd = command;
|
||||||
size_t end = cmd.find_last_not_of(" \t\n\r");
|
size_t end = cmd.find_last_not_of(" \t\n\r");
|
||||||
@ -104,7 +107,7 @@ std::string CommandProcessor::process_command(const std::string& command) {
|
|||||||
|
|
||||||
if(script_id > 0) {
|
if(script_id > 0) {
|
||||||
bool is_remote = (_session_machine != _home_machine);
|
bool is_remote = (_session_machine != _home_machine);
|
||||||
sol::object result = _lua->execute(script_content, *this, args, is_remote);
|
sol::object result = lua.execute(script_content, *this, args, is_remote);
|
||||||
return result.is<std::string>() ? result.as<std::string>() : "[Script returned an unexpected type]";
|
return result.is<std::string>() ? result.as<std::string>() : "[Script returned an unexpected type]";
|
||||||
}
|
}
|
||||||
return "Unknown command: " + command_name + "\n";
|
return "Unknown command: " + command_name + "\n";
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <lua_processor.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "db/database_manager.h"
|
#include "db/database_manager.h"
|
||||||
|
#include "i_network_bridge.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
#include "machine_manager.h"
|
#include "machine_manager.h"
|
||||||
#include "vfs.h"
|
|
||||||
|
class LuaProcessor;
|
||||||
|
|
||||||
class CommandProcessor {
|
class CommandProcessor {
|
||||||
public:
|
public:
|
||||||
CommandProcessor(Machine* home_machine, std::map<std::string, Machine*>& world_machines,
|
CommandProcessor(Machine* home_machine, DatabaseManager* db_manager,
|
||||||
DatabaseManager* db_manager, MachineManager* machine_manager);
|
MachineManager* machine_manager, INetworkBridge* network_bridge);
|
||||||
~CommandProcessor(void);
|
~CommandProcessor(void);
|
||||||
|
|
||||||
std::string process_command(const std::string& command);
|
std::string process_command(const std::string& command);
|
||||||
@ -23,9 +23,9 @@ public:
|
|||||||
vfs_node* get_current_dir(void);
|
vfs_node* get_current_dir(void);
|
||||||
Machine* get_home_machine(void);
|
Machine* get_home_machine(void);
|
||||||
Machine* get_session_machine(void);
|
Machine* get_session_machine(void);
|
||||||
std::map<std::string, Machine*>& get_world_machines(void);
|
|
||||||
DatabaseManager* get_db_manager(void);
|
DatabaseManager* get_db_manager(void);
|
||||||
MachineManager* get_machine_manager(void);
|
MachineManager* get_machine_manager(void);
|
||||||
|
INetworkBridge* get_network_bridge(void);
|
||||||
|
|
||||||
void set_current_dir(vfs_node* node);
|
void set_current_dir(vfs_node* node);
|
||||||
void set_session_machine(Machine* machine);
|
void set_session_machine(Machine* machine);
|
||||||
@ -33,9 +33,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
MachineManager* _machine_manager;
|
MachineManager* _machine_manager;
|
||||||
DatabaseManager* _db_manager;
|
DatabaseManager* _db_manager;
|
||||||
|
INetworkBridge* _network_bridge;
|
||||||
Machine* _home_machine;
|
Machine* _home_machine;
|
||||||
Machine* _session_machine;
|
Machine* _session_machine;
|
||||||
vfs_node* _current_dir;
|
vfs_node* _current_dir;
|
||||||
std::map<std::string, Machine*>& _world_machines;
|
|
||||||
LuaProcessor* _lua;
|
LuaProcessor* _lua;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,13 +8,7 @@ DatabaseManager::DatabaseManager(const std::string& db_path) :
|
|||||||
_machine_repository = std::make_unique<MachineRepository>(_db);
|
_machine_repository = std::make_unique<MachineRepository>(_db);
|
||||||
_service_repository = std::make_unique<ServiceRepository>(_db);
|
_service_repository = std::make_unique<ServiceRepository>(_db);
|
||||||
_vfs_repository = std::make_unique<VFSRepository>(_db);
|
_vfs_repository = std::make_unique<VFSRepository>(_db);
|
||||||
}
|
|
||||||
|
|
||||||
DatabaseManager::~DatabaseManager(void) {
|
|
||||||
/* db is auto closed when _db goes out of scope. */
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatabaseManager::init(void) {
|
|
||||||
_db << "CREATE TABLE IF NOT EXISTS players("
|
_db << "CREATE TABLE IF NOT EXISTS players("
|
||||||
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
|
||||||
"username TEXT NOT NULL UNIQUE,"
|
"username TEXT NOT NULL UNIQUE,"
|
||||||
@ -46,6 +40,10 @@ void DatabaseManager::init(void) {
|
|||||||
");";
|
");";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DatabaseManager::~DatabaseManager(void) {
|
||||||
|
/* db is auto closed when _db goes out of scope. */
|
||||||
|
}
|
||||||
|
|
||||||
bool DatabaseManager::create_player(const std::string& username, const std::string& password,
|
bool DatabaseManager::create_player(const std::string& username, const std::string& password,
|
||||||
const std::string& hostname, vfs_node* vfs_template) {
|
const std::string& hostname, vfs_node* vfs_template) {
|
||||||
long long player_id = 0;
|
long long player_id = 0;
|
||||||
|
|||||||
@ -15,8 +15,6 @@ public:
|
|||||||
DatabaseManager(const std::string& db_path);
|
DatabaseManager(const std::string& db_path);
|
||||||
~DatabaseManager(void);
|
~DatabaseManager(void);
|
||||||
|
|
||||||
void init(void);
|
|
||||||
|
|
||||||
/* Return true on success, false if user already exists. */
|
/* Return true on success, false if user already exists. */
|
||||||
bool create_player(const std::string& username, const std::string& password,
|
bool create_player(const std::string& username, const std::string& password,
|
||||||
const std::string& hostname, vfs_node* vfs_template);
|
const std::string& hostname, vfs_node* vfs_template);
|
||||||
|
|||||||
@ -31,6 +31,15 @@ std::vector<MachineData> MachineRepository::get_all_npcs(void) {
|
|||||||
return machines;
|
return machines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<MachineData> MachineRepository::get_all(void) {
|
||||||
|
std::vector<MachineData> machines;
|
||||||
|
_db << "SELECT id, ip_address FROM machines;"
|
||||||
|
>> [&](long long id, std::string ip_address) {
|
||||||
|
machines.push_back({id, ip_address});
|
||||||
|
};
|
||||||
|
return machines;
|
||||||
|
}
|
||||||
|
|
||||||
std::string MachineRepository::get_hostname(long long machine_id) {
|
std::string MachineRepository::get_hostname(long long machine_id) {
|
||||||
std::string hostname;
|
std::string hostname;
|
||||||
_db << "SELECT hostname FROM machines WHERE id = ?;"
|
_db << "SELECT hostname FROM machines WHERE id = ?;"
|
||||||
|
|||||||
@ -19,6 +19,7 @@ public:
|
|||||||
const std::string& ip_address);
|
const std::string& ip_address);
|
||||||
int get_npc_count(void);
|
int get_npc_count(void);
|
||||||
std::vector<MachineData> get_all_npcs(void);
|
std::vector<MachineData> get_all_npcs(void);
|
||||||
|
std::vector<MachineData> get_all(void);
|
||||||
std::string get_hostname(long long machine_id);
|
std::string get_hostname(long long machine_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
11
common/src/i_network_bridge.h
Normal file
11
common/src/i_network_bridge.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Machine;
|
||||||
|
|
||||||
|
class INetworkBridge {
|
||||||
|
public:
|
||||||
|
virtual Machine* get_machine_by_ip(const std::string& ip) = 0;
|
||||||
|
virtual void release_machine(long long machine_id) = 0;
|
||||||
|
};
|
||||||
@ -1,9 +1,9 @@
|
|||||||
#include <sol/types.hpp>
|
#include <sol/types.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "i_network_bridge.h"
|
||||||
#include "lua_api.h"
|
#include "lua_api.h"
|
||||||
#include "command_processor.h"
|
#include "command_processor.h"
|
||||||
#include "db/database_manager.h"
|
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
|
|
||||||
@ -15,45 +15,34 @@ vfs_node* get_current_dir(CommandProcessor& context) {
|
|||||||
|
|
||||||
std::string rm(CommandProcessor& context, const std::string& filename) {
|
std::string rm(CommandProcessor& context, const std::string& filename) {
|
||||||
vfs_node* current_dir = context.get_current_dir();
|
vfs_node* current_dir = context.get_current_dir();
|
||||||
DatabaseManager* db = context.get_db_manager();
|
auto it = current_dir->children.find(filename);
|
||||||
|
|
||||||
/* find the file in the database. */
|
if(it == current_dir->children.end()) {
|
||||||
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.";
|
return "rm: cannot remove '" + filename + "': No such file or directory.";
|
||||||
}
|
}
|
||||||
if(file_type == DIR_NODE) {
|
if(it->second->type == DIR_NODE) {
|
||||||
return "rm: cannot remove '" + filename + "': Is a directory.";
|
return "rm: cannot remove '" + filename + "': Is a directory.";
|
||||||
}
|
}
|
||||||
|
|
||||||
db->_db << "DELETE FROM vfs_nodes WHERE id = ?;" << file_id;
|
delete it->second; /* Free the memory for the node. */
|
||||||
/* Also remove from in-mem VFS. */
|
current_dir->children.erase(it); /* Remove from map. */
|
||||||
delete current_dir->children[filename];
|
|
||||||
current_dir->children.erase(filename);
|
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string write_file(CommandProcessor& context, const std::string& filename,
|
std::string write_file(CommandProcessor& context, const std::string& filename,
|
||||||
const std::string& content) {
|
const std::string& content) {
|
||||||
vfs_node* current_dir = context.get_current_dir();
|
vfs_node* current_dir = context.get_current_dir();
|
||||||
DatabaseManager* db = context.get_db_manager();
|
auto it = current_dir->children.find(filename);
|
||||||
|
|
||||||
if(current_dir->children.count(filename)) {
|
if(it != current_dir->children.end()) {
|
||||||
vfs_node* target_node = current_dir->children[filename];
|
/* File exists, update content. */
|
||||||
db->_db << "UPDATE vfs_nodes SET content = ? WHERE id = ?;"
|
if(it->second->type == DIR_NODE) {
|
||||||
<< content << target_node->id;
|
return "write: " + filename + ": Is a directory.";
|
||||||
target_node->content = content;
|
}
|
||||||
|
it->second->content = content;
|
||||||
} else {
|
} else {
|
||||||
db->_db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type, content) VALUES (?, ?, ?, ?, ?);"
|
/* File does not exist, create it. */
|
||||||
<< context.get_session_machine()->id << current_dir->id << filename << FILE_NODE << content;
|
|
||||||
vfs_node* new_file = new_node(filename, FILE_NODE, current_dir);
|
vfs_node* new_file = new_node(filename, FILE_NODE, current_dir);
|
||||||
new_file->id = db->_db.last_insert_rowid();
|
|
||||||
new_file->content = content;
|
new_file->content = content;
|
||||||
current_dir->children[filename] = new_file;
|
current_dir->children[filename] = new_file;
|
||||||
}
|
}
|
||||||
@ -62,24 +51,15 @@ std::string write_file(CommandProcessor& context, const std::string& filename,
|
|||||||
|
|
||||||
std::string cd(CommandProcessor& context, const std::string& path) {
|
std::string cd(CommandProcessor& context, const std::string& path) {
|
||||||
vfs_node* current_dir = context.get_current_dir();
|
vfs_node* current_dir = context.get_current_dir();
|
||||||
DatabaseManager* db = context.get_db_manager();
|
|
||||||
|
|
||||||
if(path == "..") {
|
if(path == "..") {
|
||||||
if(current_dir->parent) {
|
if(current_dir->parent) {
|
||||||
context.set_current_dir(current_dir->parent);
|
context.set_current_dir(current_dir->parent);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
long long target_id = 0;
|
auto it = current_dir->children.find(path);
|
||||||
db->_db << "SELECT id FROM vfs_nodes WHERE parent_id = ? AND name = ? AND type = 1;"
|
if(it != current_dir->children.end() && it->second->type == DIR_NODE) {
|
||||||
<< current_dir->id << path
|
context.set_current_dir(it->second);
|
||||||
>> 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 {
|
} else {
|
||||||
return "cd: no such file or directory: " + path;
|
return "cd: no such file or directory: " + path;
|
||||||
}
|
}
|
||||||
@ -89,52 +69,59 @@ std::string cd(CommandProcessor& context, const std::string& path) {
|
|||||||
|
|
||||||
std::string ls(CommandProcessor& context) {
|
std::string ls(CommandProcessor& context) {
|
||||||
vfs_node* dir = context.get_current_dir();
|
vfs_node* dir = context.get_current_dir();
|
||||||
printf("DEBUG: ls command called for directory_id: %lld\n", dir->id);
|
|
||||||
if(dir->type != DIR_NODE) {
|
if(dir->type != DIR_NODE) {
|
||||||
return "ls: not a directory";
|
return "ls: not a directory";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
for(auto const& [name, node] : dir->children) {
|
||||||
context.get_db_manager()->_db << "SELECT name, type FROM vfs_nodes WHERE parent_id = ?;"
|
|
||||||
<< dir->id
|
|
||||||
>> [&](std::string name, int type) {
|
|
||||||
ss << name;
|
ss << name;
|
||||||
if(type == DIR_NODE) ss << "/";
|
if(node->type == DIR_NODE) {
|
||||||
|
ss << "/";
|
||||||
|
}
|
||||||
ss << " ";
|
ss << " ";
|
||||||
};
|
}
|
||||||
|
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ssh(CommandProcessor& context, const std::string& ip) {
|
std::string ssh(CommandProcessor& context, const std::string& ip) {
|
||||||
if(context.get_world_machines().count(ip)) {
|
INetworkBridge* bridge = context.get_network_bridge();
|
||||||
context.set_session_machine(context.get_world_machines().at(ip));
|
Machine* target_machine = bridge->get_machine_by_ip(ip);
|
||||||
|
|
||||||
|
if(target_machine) {
|
||||||
|
context.set_session_machine(target_machine);
|
||||||
return "Connected to " + ip;
|
return "Connected to " + ip;
|
||||||
}
|
} else {
|
||||||
return "ssh: Could not resolve hostname " + ip + ": Name or service not found";
|
return "ssh: Could not resolve hostname " + ip + ": Name or service not found";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string nmap(CommandProcessor& context, const std::string& ip) {
|
std::string nmap(CommandProcessor& context, const std::string& ip) {
|
||||||
if(!context.get_world_machines().count(ip)) {
|
long long machine_id = context.get_machine_manager()->get_machine_id_by_ip(ip);
|
||||||
|
if(machine_id == 0) {
|
||||||
return "nmap: Could not resolve host: " + ip;
|
return "nmap: Could not resolve host: " + ip;
|
||||||
}
|
}
|
||||||
|
auto services = context.get_db_manager()->services().get_for_machine(machine_id);
|
||||||
|
|
||||||
Machine* target_machine = context.get_world_machines().at(ip);
|
if(services.empty()) {
|
||||||
if(target_machine->services.empty()) {
|
|
||||||
return "No open ports for " + ip;
|
return "No open ports for " + ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "Host: " << ip << "\n";
|
ss << "Host: " << ip << "\n";
|
||||||
ss << "PORT\tSTATE\tSERVICE\n";
|
ss << "PORT\tSTATE\tSERVICE\n";
|
||||||
for(auto const& [port, service_name] : target_machine->services) {
|
for(auto const& [port, service_name] : services) {
|
||||||
ss << port << "/tcp\t" << "open\t" << service_name << "\n";
|
ss << port << "/tcp\t" << "open\t" << service_name << "\n";
|
||||||
}
|
}
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string disconnect(CommandProcessor& context) {
|
std::string disconnect(CommandProcessor& context) {
|
||||||
|
Machine* current_machine = context.get_session_machine();
|
||||||
|
if(current_machine != context.get_home_machine()) {
|
||||||
|
INetworkBridge* bridge = context.get_network_bridge();
|
||||||
|
bridge->release_machine(current_machine->id);
|
||||||
|
}
|
||||||
context.set_session_machine(context.get_home_machine());
|
context.set_session_machine(context.get_home_machine());
|
||||||
return "Connection closed.";
|
return "Connection closed.";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
|
|
||||||
class CommandProcessor;
|
class CommandProcessor;
|
||||||
|
class NetworkManager;
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
|||||||
@ -4,11 +4,11 @@
|
|||||||
#include <sol/raii.hpp>
|
#include <sol/raii.hpp>
|
||||||
|
|
||||||
#include "lua_processor.h"
|
#include "lua_processor.h"
|
||||||
#include "vfs.h"
|
|
||||||
#include "lua_api.h"
|
#include "lua_api.h"
|
||||||
#include "command_processor.h"
|
#include "command_processor.h"
|
||||||
|
#include "vfs.h"
|
||||||
|
|
||||||
LuaProcessor::LuaProcessor(void) {
|
LuaProcessor::LuaProcessor(CommandProcessor& context) {
|
||||||
_lua.open_libraries(sol::lib::base, sol::lib::string, sol::lib::io, sol::lib::table);
|
_lua.open_libraries(sol::lib::base, sol::lib::string, sol::lib::io, sol::lib::table);
|
||||||
|
|
||||||
/* Expose vfs_node struct members to Lua. */
|
/* Expose vfs_node struct members to Lua. */
|
||||||
@ -23,15 +23,15 @@ LuaProcessor::LuaProcessor(void) {
|
|||||||
|
|
||||||
/* Create the 'bettola' API table. */
|
/* Create the 'bettola' API table. */
|
||||||
sol::table bettola_api = _lua.create_named_table("bettola");
|
sol::table bettola_api = _lua.create_named_table("bettola");
|
||||||
bettola_api["rm"] = &api::rm;
|
bettola_api.set_function("rm", &api::rm);
|
||||||
bettola_api["ls"] = &api::ls;
|
bettola_api.set_function("ls", &api::ls);
|
||||||
bettola_api["write_file"] = &api::write_file;
|
bettola_api.set_function("write_file", &api::write_file);
|
||||||
bettola_api["get_current_dir"]= &api::get_current_dir;
|
bettola_api.set_function("get_current_dir", &api::get_current_dir);
|
||||||
bettola_api["cd"] = &api::cd;
|
bettola_api.set_function("cd", &api::cd);
|
||||||
bettola_api["ssh"] = &api::ssh;
|
bettola_api.set_function("close_terminal", &api::close_terminal);
|
||||||
bettola_api["nmap"] = &api::nmap;
|
bettola_api.set_function("ssh", &api::ssh);
|
||||||
bettola_api["disconnect"] = &api::disconnect;
|
bettola_api.set_function("nmap", &api::nmap);
|
||||||
bettola_api["close_terminal"] = &api::close_terminal;
|
bettola_api.set_function("disconnect", &api::disconnect);
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaProcessor::~LuaProcessor(void) {}
|
LuaProcessor::~LuaProcessor(void) {}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ class CommandProcessor;
|
|||||||
|
|
||||||
class LuaProcessor {
|
class LuaProcessor {
|
||||||
public:
|
public:
|
||||||
LuaProcessor(void);
|
LuaProcessor(CommandProcessor& context);
|
||||||
~LuaProcessor(void);
|
~LuaProcessor(void);
|
||||||
|
|
||||||
/* Executes a string of lua code and returns result as a string. */
|
/* Executes a string of lua code and returns result as a string. */
|
||||||
|
|||||||
@ -96,7 +96,7 @@ void build_tree(vfs_node* parent, const std::map<long long, vfs_node*>& nodes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Machine* MachineManager::load_machine(long long machine_id, DatabaseManager* db_manager) {
|
Machine* MachineManager::load_machine(long long machine_id) {
|
||||||
printf("DEBUG: load_machine called for machine_id: %lld\n", machine_id);
|
printf("DEBUG: load_machine called for machine_id: %lld\n", machine_id);
|
||||||
|
|
||||||
std::string hostname = _db_manager->machines().get_hostname(machine_id);
|
std::string hostname = _db_manager->machines().get_hostname(machine_id);
|
||||||
@ -107,7 +107,7 @@ Machine* MachineManager::load_machine(long long machine_id, DatabaseManager* db_
|
|||||||
std::map<long long, vfs_node*> node_map;
|
std::map<long long, vfs_node*> node_map;
|
||||||
vfs_node* root = nullptr;
|
vfs_node* root = nullptr;
|
||||||
|
|
||||||
auto nodes = db_manager->vfs().get_nodes_for_machine(machine_id);
|
auto nodes = _db_manager->vfs().get_nodes_for_machine(machine_id);
|
||||||
for(vfs_node* node : nodes) {
|
for(vfs_node* node : nodes) {
|
||||||
node_map[node->id] = node;
|
node_map[node->id] = node;
|
||||||
if(node->name == "/") {
|
if(node->name == "/") {
|
||||||
@ -123,3 +123,17 @@ Machine* MachineManager::load_machine(long long machine_id, DatabaseManager* db_
|
|||||||
}
|
}
|
||||||
return machine;
|
return machine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long long MachineManager::get_machine_id_by_ip(const std::string& ip) {
|
||||||
|
if(_ip_to_id_map.count(ip)) {
|
||||||
|
return _ip_to_id_map[ip];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MachineManager::init(void) {
|
||||||
|
auto all_machines = _db_manager->machines().get_all();
|
||||||
|
for(const auto& machine_data : all_machines) {
|
||||||
|
_ip_to_id_map[machine_data.ip_address] = machine_data.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "db/database_manager.h"
|
#include "db/database_manager.h"
|
||||||
@ -16,12 +17,20 @@ public:
|
|||||||
|
|
||||||
Machine* create_machine(uint32_t id, const std::string& hostname,
|
Machine* create_machine(uint32_t id, const std::string& hostname,
|
||||||
const std::string& system_type);
|
const std::string& system_type);
|
||||||
Machine* load_machine(long long machine_id, DatabaseManager* db_manager);
|
Machine* load_machine(long long machine_id);
|
||||||
|
void init(void);
|
||||||
|
|
||||||
vfs_node* get_vfs_template(void) { return _vfs_template_root; }
|
vfs_node* get_vfs_template(void) { return _vfs_template_root; }
|
||||||
|
|
||||||
|
/* Machine lookup. */
|
||||||
|
long long get_machine_id_by_ip(const std::string& ip);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DatabaseManager* _db_manager;
|
DatabaseManager* _db_manager;
|
||||||
vfs_node* _vfs_template_root;
|
vfs_node* _vfs_template_root;
|
||||||
|
|
||||||
|
/* In memory cache of all machines, loaded or not. */
|
||||||
|
std::map<std::string, long long> _ip_to_id_map;
|
||||||
|
std::map<long long, Machine*> _world_machines;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -16,26 +16,18 @@
|
|||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
|
|
||||||
NetworkManager::NetworkManager(const std::string& db_path) :
|
NetworkManager::NetworkManager(const std::string& db_path) :
|
||||||
|
_io_context(),
|
||||||
_db_manager(std::make_unique<DatabaseManager>(db_path)),
|
_db_manager(std::make_unique<DatabaseManager>(db_path)),
|
||||||
_acceptor(_io_context),
|
_acceptor(_io_context),
|
||||||
_machine_manager(_db_manager.get()) {
|
_machine_manager(_db_manager.get()) {
|
||||||
|
|
||||||
_db_manager->init();
|
|
||||||
_seed_npc_machines();
|
_seed_npc_machines();
|
||||||
|
_machine_manager.init();
|
||||||
/* Load NPC machines from the database. */
|
|
||||||
auto npc_machines = _db_manager->machines().get_all_npcs();
|
|
||||||
for(const auto& machine_data : npc_machines) {
|
|
||||||
_world_machines[machine_data.ip_address] =
|
|
||||||
_machine_manager.load_machine(machine_data.id, _db_manager.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Created world with %zu networks\n", _world_machines.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkManager::~NetworkManager(void) {
|
NetworkManager::~NetworkManager(void) {
|
||||||
for(auto const& [ip, machine] : _world_machines) {
|
for(auto& pair : _active_machines) {
|
||||||
delete machine;
|
_save_machine_vfs(pair.second.machine.get());
|
||||||
}
|
}
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
@ -76,13 +68,10 @@ void NetworkManager::start_accept(void) {
|
|||||||
auto new_connection =
|
auto new_connection =
|
||||||
std::make_shared<net::TcpConnection>(_io_context, std::move(socket));
|
std::make_shared<net::TcpConnection>(_io_context, std::move(socket));
|
||||||
|
|
||||||
/* Create a new player for this connection. */
|
/* Create a new player for this connection. pendnding authentication.*/
|
||||||
uint32_t player_id = _next_player_id++;
|
uint32_t player_id = _next_player_id++;
|
||||||
Machine* player_machine = _machine_manager.create_machine(player_id, "player.home",
|
auto new_player = std::make_unique<Player>(player_id, nullptr, _db_manager.get(),
|
||||||
"player");
|
&_machine_manager, this);
|
||||||
auto new_player = std::make_unique<Player>(player_id, player_machine,
|
|
||||||
_world_machines, _db_manager.get(),
|
|
||||||
&_machine_manager);
|
|
||||||
Player* new_player_ptr = new_player.get();
|
Player* new_player_ptr = new_player.get();
|
||||||
_players[player_id] = std::move(new_player);
|
_players[player_id] = std::move(new_player);
|
||||||
new_connection->set_id(player_id);
|
new_connection->set_id(player_id);
|
||||||
@ -123,12 +112,11 @@ void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
|
|||||||
if(args.size() == 3) {
|
if(args.size() == 3) {
|
||||||
if(_db_manager->create_player(args[0], args[1], args[2],
|
if(_db_manager->create_player(args[0], args[1], args[2],
|
||||||
_machine_manager.get_vfs_template())) {
|
_machine_manager.get_vfs_template())) {
|
||||||
long long machine_id = _db_manager->players().get_home_machine_id(args[0]);
|
long long home_machine_id = _db_manager->players().get_home_machine_id(args[0]);
|
||||||
Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
|
Machine* home_machine = _get_or_load_machine(home_machine_id);
|
||||||
delete player->cmd_processor; /* Delete old command processor. */
|
delete player->cmd_processor; /* Delete old command processor. */
|
||||||
player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
|
player->cmd_processor = new CommandProcessor(home_machine, _db_manager.get(),
|
||||||
_db_manager.get(),
|
&_machine_manager, this);
|
||||||
&_machine_manager);
|
|
||||||
player->state = PlayerState::ACTIVE;
|
player->state = PlayerState::ACTIVE;
|
||||||
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS));
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_CREATE_ACCOUNT_SUCCESS));
|
||||||
/* send initial prompt. */
|
/* send initial prompt. */
|
||||||
@ -144,13 +132,11 @@ void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
|
|||||||
case net_protocol::Opcode::C2S_LOGIN:
|
case net_protocol::Opcode::C2S_LOGIN:
|
||||||
if(args.size() == 2) {
|
if(args.size() == 2) {
|
||||||
if(_db_manager->players().authenticate(args[0], args[1])) {
|
if(_db_manager->players().authenticate(args[0], args[1])) {
|
||||||
long long machine_id = _db_manager->players().get_home_machine_id(args[0]);
|
long long home_machine_id = _db_manager->players().get_home_machine_id(args[0]);
|
||||||
printf("DEBUG: Loading machine %lld for player %s\n", machine_id, args[0].c_str());
|
Machine* home_machine = _get_or_load_machine(home_machine_id);
|
||||||
Machine* home_machine = _machine_manager.load_machine(machine_id, _db_manager.get());
|
|
||||||
delete player->cmd_processor; /* Delete old command processor. */
|
delete player->cmd_processor; /* Delete old command processor. */
|
||||||
player->cmd_processor = new CommandProcessor(home_machine, _world_machines,
|
player->cmd_processor = new CommandProcessor(home_machine, _db_manager.get(),
|
||||||
_db_manager.get(), &_machine_manager);
|
&_machine_manager, this);
|
||||||
|
|
||||||
player->state = PlayerState::ACTIVE;
|
player->state = PlayerState::ACTIVE;
|
||||||
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_LOGIN_SUCCESS));
|
connection->send(net_protocol::build_message(net_protocol::Opcode::S2C_LOGIN_SUCCESS));
|
||||||
/* Send initial prompt. */
|
/* Send initial prompt. */
|
||||||
@ -214,6 +200,20 @@ void NetworkManager::on_message(std::shared_ptr<net::TcpConnection> connection,
|
|||||||
|
|
||||||
void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connection) {
|
void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connection) {
|
||||||
uint32_t player_id = connection->get_id();
|
uint32_t player_id = connection->get_id();
|
||||||
|
Player* player = _players[player_id].get();
|
||||||
|
|
||||||
|
if(player && player->cmd_processor) {
|
||||||
|
Machine* home_machine = player->cmd_processor->get_home_machine();
|
||||||
|
Machine* session_machine = player->cmd_processor->get_session_machine();
|
||||||
|
|
||||||
|
if(home_machine) {
|
||||||
|
release_machine(home_machine->id);
|
||||||
|
}
|
||||||
|
if(session_machine && session_machine != home_machine) {
|
||||||
|
release_machine(session_machine->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fprintf(stderr, "[Player %u] Disconnected.\n", player_id);
|
fprintf(stderr, "[Player %u] Disconnected.\n", player_id);
|
||||||
_connections.erase(
|
_connections.erase(
|
||||||
std::remove(_connections.begin(), _connections.end(), connection), _connections.end());
|
std::remove(_connections.begin(), _connections.end(), connection), _connections.end());
|
||||||
@ -221,6 +221,67 @@ void NetworkManager::on_disconnect(std::shared_ptr<net::TcpConnection> connectio
|
|||||||
_players.erase(player_id);
|
_players.erase(player_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Machine* NetworkManager::get_machine_by_ip(const std::string& ip) {
|
||||||
|
long long machine_id = _machine_manager.get_machine_id_by_ip(ip);
|
||||||
|
if(machine_id != 0) {
|
||||||
|
return _get_or_load_machine(machine_id);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Machine* NetworkManager::_get_or_load_machine(long long machine_id) {
|
||||||
|
if(_active_machines.count(machine_id)) {
|
||||||
|
_active_machines[machine_id].ref_count++;
|
||||||
|
return _active_machines[machine_id].machine.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Machine not in cache, load it. */
|
||||||
|
fprintf(stderr, "Loading machine %lld into cache.\n", machine_id);
|
||||||
|
Machine* loaded_machine = _machine_manager.load_machine(machine_id);
|
||||||
|
_active_machines[machine_id] = {
|
||||||
|
std::unique_ptr<Machine>(loaded_machine), 1
|
||||||
|
};
|
||||||
|
return loaded_machine;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkManager::release_machine(long long machine_id) {
|
||||||
|
if(!_active_machines.count(machine_id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_active_machines[machine_id].ref_count--;
|
||||||
|
fprintf(stderr, "Releasing machine %lld, ref count now %d.\n", machine_id,
|
||||||
|
_active_machines[machine_id].ref_count);
|
||||||
|
|
||||||
|
if(_active_machines[machine_id].ref_count <= 0) {
|
||||||
|
fprintf(stderr, "Machine %lld has no more references, saving and evicting from cache.\n",
|
||||||
|
machine_id);
|
||||||
|
_save_machine_vfs(_active_machines[machine_id].machine.get());
|
||||||
|
_active_machines.erase(machine_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkManager::_recursive_save_vfs(long long machine_id, vfs_node* node, long long* parent_id) {
|
||||||
|
long long current_node_id = _db_manager->vfs().create_node(machine_id, parent_id,
|
||||||
|
node->name, node->type,
|
||||||
|
node->content);
|
||||||
|
|
||||||
|
if(node->type == DIR_NODE) {
|
||||||
|
for(auto const& [name, child] : node->children) {
|
||||||
|
_recursive_save_vfs(machine_id, child, ¤t_node_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkManager::_save_machine_vfs(Machine* machine) {
|
||||||
|
if(!machine || !machine->vfs_root) return;
|
||||||
|
|
||||||
|
_db_manager->_db << "BEGIN;";
|
||||||
|
_db_manager->_db << "DELETE FROM vfs_nodes WHERE machine_id = ?;" << machine->id;
|
||||||
|
_recursive_save_vfs(machine->id, machine->vfs_root, nullptr);
|
||||||
|
_db_manager->_db << "COMMIT;";
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkManager::_seed_npc_machines(void) {
|
void NetworkManager::_seed_npc_machines(void) {
|
||||||
int npc_count = _db_manager->machines().get_npc_count();
|
int npc_count = _db_manager->machines().get_npc_count();
|
||||||
|
|
||||||
|
|||||||
@ -10,11 +10,17 @@
|
|||||||
#include "asio/io_context.hpp"
|
#include "asio/io_context.hpp"
|
||||||
#include "net/tcp_connection.h"
|
#include "net/tcp_connection.h"
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
|
#include "i_network_bridge.h"
|
||||||
#include "machine_manager.h"
|
#include "machine_manager.h"
|
||||||
|
|
||||||
class DatabaseManager;
|
class DatabaseManager;
|
||||||
|
|
||||||
class NetworkManager {
|
struct CachedMachine {
|
||||||
|
std::unique_ptr<Machine> machine;
|
||||||
|
int ref_count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkManager : public INetworkBridge {
|
||||||
public:
|
public:
|
||||||
NetworkManager(const std::string& db_path);
|
NetworkManager(const std::string& db_path);
|
||||||
~NetworkManager(void);
|
~NetworkManager(void);
|
||||||
@ -22,11 +28,17 @@ public:
|
|||||||
void start(uint16_t port);
|
void start(uint16_t port);
|
||||||
void stop(void);
|
void stop(void);
|
||||||
|
|
||||||
|
Machine* get_machine_by_ip(const std::string& ip) override;
|
||||||
|
void release_machine(long long machine_id) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void start_accept(void);
|
void start_accept(void);
|
||||||
void on_message(std::shared_ptr<net::TcpConnection> connection,
|
void on_message(std::shared_ptr<net::TcpConnection> connection,
|
||||||
const std::string& message);
|
const std::string& message);
|
||||||
void on_disconnect(std::shared_ptr<net::TcpConnection> connection);
|
void on_disconnect(std::shared_ptr<net::TcpConnection> connection);
|
||||||
|
void _save_machine_vfs(Machine* machine);
|
||||||
|
void _recursive_save_vfs(long long machine_id, vfs_node* node,
|
||||||
|
long long* parent_id);
|
||||||
|
|
||||||
void _seed_npc_machines(void);
|
void _seed_npc_machines(void);
|
||||||
|
|
||||||
@ -36,9 +48,12 @@ private:
|
|||||||
|
|
||||||
std::deque<std::shared_ptr<net::TcpConnection>> _connections;
|
std::deque<std::shared_ptr<net::TcpConnection>> _connections;
|
||||||
std::unordered_map<uint32_t, std::unique_ptr<Player>> _players;
|
std::unordered_map<uint32_t, std::unique_ptr<Player>> _players;
|
||||||
|
std::map<long long, CachedMachine> _active_machines;
|
||||||
uint32_t _next_player_id = 1;
|
uint32_t _next_player_id = 1;
|
||||||
|
|
||||||
std::map<std::string, Machine*> _world_machines; /* For NPC's. */
|
|
||||||
std::unique_ptr<DatabaseManager> _db_manager;
|
std::unique_ptr<DatabaseManager> _db_manager;
|
||||||
MachineManager _machine_manager;
|
MachineManager _machine_manager;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Machine* _get_or_load_machine(long long machine_id);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,25 +1,20 @@
|
|||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "command_processor.h"
|
#include "command_processor.h"
|
||||||
#include "db/database_manager.h"
|
#include "db/database_manager.h"
|
||||||
|
#include "i_network_bridge.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
|
|
||||||
Player::Player(uint32_t new_id, Machine* home_machine,
|
Player::Player(uint32_t new_id, Machine* home_machine,
|
||||||
std::map<std::string, Machine*>& world_machines, DatabaseManager* db_manager,
|
DatabaseManager* db_manager,
|
||||||
MachineManager* machine_manager) :
|
MachineManager* machine_manager,
|
||||||
|
INetworkBridge* network_bridge) :
|
||||||
id(new_id),
|
id(new_id),
|
||||||
state(PlayerState::AUTHENTICATING) {
|
state(PlayerState::AUTHENTICATING) {
|
||||||
|
|
||||||
cmd_processor = new CommandProcessor(home_machine, world_machines, db_manager, machine_manager);
|
cmd_processor = new CommandProcessor(home_machine, db_manager, machine_manager,
|
||||||
|
network_bridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
Player::~Player(void) {
|
Player::~Player(void) {
|
||||||
if(cmd_processor) {
|
|
||||||
/*
|
|
||||||
* Player is owner of their own machine. CommandProcessor
|
|
||||||
* holds the most current pointer to it (in case of CoW).
|
|
||||||
* Delete the machine first, then the processor point to it.
|
|
||||||
*/
|
|
||||||
delete cmd_processor->get_home_machine();
|
|
||||||
delete cmd_processor;
|
delete cmd_processor;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "db/database_manager.h"
|
#include "db/database_manager.h"
|
||||||
#include "command_processor.h"
|
#include "command_processor.h"
|
||||||
|
#include "i_network_bridge.h"
|
||||||
#include "machine.h"
|
#include "machine.h"
|
||||||
#include "machine_manager.h"
|
#include "machine_manager.h"
|
||||||
|
|
||||||
@ -16,9 +15,8 @@ enum class PlayerState {
|
|||||||
|
|
||||||
class Player {
|
class Player {
|
||||||
public:
|
public:
|
||||||
Player(uint32_t id, Machine* home_machine, std::map<std::string,
|
Player(uint32_t id, Machine* home_machine, DatabaseManager* db_manager,
|
||||||
Machine*>& world_machines, DatabaseManager* db_manager,
|
MachineManager* machine_manager, INetworkBridge* network_bridge);
|
||||||
MachineManager* machine_manager);
|
|
||||||
~Player(void);
|
~Player(void);
|
||||||
|
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user