Refactor PlayerAttributes code

This commit is contained in:
sfan5
2025-02-19 17:42:34 +01:00
parent d9c89bd6a2
commit 7a0bc15d21
4 changed files with 150 additions and 119 deletions

View File

@ -2,12 +2,129 @@
#include <sstream>
#include <stdexcept>
#include <dirent.h>
#include <unistd.h> // for usleep
#include <sqlite3.h>
#include <unistd.h> // usleep
#include "config.h"
#include "PlayerAttributes.h"
#include "util.h"
#include "db-sqlite3.h" // SQLite3Base
namespace {
bool parse_pos(std::string position, Player &dst)
{
if (position.empty())
return false;
if (position.front() == '(' && position.back() == ')')
position = position.substr(1, position.size() - 2);
std::istringstream iss(position);
if (!(iss >> dst.x))
return false;
if (iss.get() != ',')
return false;
if (!(iss >> dst.y))
return false;
if (iss.get() != ',')
return false;
if (!(iss >> dst.z))
return false;
return iss.eof();
}
// Helper classes per backend
class FilesReader {
std::string path;
DIR *dir = nullptr;
public:
FilesReader(const std::string &path) : path(path) {
dir = opendir(path.c_str());
}
~FilesReader() {
if (dir)
closedir(dir);
}
void read(PlayerAttributes::Players &dest);
};
class SQLiteReader : SQLite3Base {
sqlite3_stmt *stmt_get_player_pos = NULL;
public:
SQLiteReader(const std::string &database) {
openDatabase(database.c_str());
}
~SQLiteReader() {
sqlite3_finalize(stmt_get_player_pos);
}
void read(PlayerAttributes::Players &dest);
};
}
void FilesReader::read(PlayerAttributes::Players &dest)
{
if (!dir)
return;
struct dirent *ent;
std::string name, position;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.')
continue;
std::ifstream in(path + PATH_SEPARATOR + ent->d_name);
if (!in.good())
continue;
name = read_setting("name", in);
position = read_setting("position", in);
Player player;
player.name = name;
if (!parse_pos(position, player)) {
std::cerr << "Failed to parse position " << position << std::endl;
continue;
}
player.x /= 10.0f;
player.y /= 10.0f;
player.z /= 10.0f;
dest.push_back(std::move(player));
}
}
#define SQLRES(r, good) check_result(r, good)
#define SQLOK(r) SQLRES(r, SQLITE_OK)
void SQLiteReader::read(PlayerAttributes::Players &dest)
{
SQLOK(prepare(stmt_get_player_pos,
"SELECT name, posX, posY, posZ FROM player"));
int result;
while ((result = sqlite3_step(stmt_get_player_pos)) != SQLITE_DONE) {
if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
} else if (result != SQLITE_ROW) {
throw std::runtime_error(sqlite3_errmsg(db));
}
Player player;
player.name = read_str(stmt_get_player_pos, 0);
player.x = sqlite3_column_double(stmt_get_player_pos, 1);
player.y = sqlite3_column_double(stmt_get_player_pos, 2);
player.z = sqlite3_column_double(stmt_get_player_pos, 3);
player.x /= 10.0f;
player.y /= 10.0f;
player.z /= 10.0f;
dest.push_back(std::move(player));
}
}
/**********/
PlayerAttributes::PlayerAttributes(const std::string &worldDir)
{
@ -18,107 +135,13 @@ PlayerAttributes::PlayerAttributes(const std::string &worldDir)
ifs.close();
if (backend == "files")
readFiles(worldDir + "players");
FilesReader(worldDir + "players").read(m_players);
else if (backend == "sqlite3")
readSqlite(worldDir + "players.sqlite");
SQLiteReader(worldDir + "players.sqlite").read(m_players);
else
throw std::runtime_error(std::string("Unknown player backend: ") + backend);
}
void PlayerAttributes::readFiles(const std::string &playersPath)
{
DIR *dir;
dir = opendir (playersPath.c_str());
if (!dir)
return;
struct dirent *ent;
while ((ent = readdir (dir)) != NULL) {
if (ent->d_name[0] == '.')
continue;
std::ifstream in(playersPath + PATH_SEPARATOR + ent->d_name);
if (!in.good())
continue;
std::string name, position;
name = read_setting("name", in);
in.seekg(0);
position = read_setting("position", in);
Player player;
std::istringstream iss(position);
char tmp;
iss >> tmp; // '('
iss >> player.x;
iss >> tmp; // ','
iss >> player.y;
iss >> tmp; // ','
iss >> player.z;
iss >> tmp; // ')'
if (tmp != ')')
continue;
player.name = name;
player.x /= 10.0f;
player.y /= 10.0f;
player.z /= 10.0f;
m_players.push_back(player);
}
closedir(dir);
}
/**********/
#define SQLRES(f, good) \
result = (sqlite3_##f); \
if (result != good) { \
throw std::runtime_error(sqlite3_errmsg(db));\
}
#define SQLOK(f) SQLRES(f, SQLITE_OK)
void PlayerAttributes::readSqlite(const std::string &db_name)
{
int result;
sqlite3 *db;
sqlite3_stmt *stmt_get_player_pos;
SQLOK(open_v2(db_name.c_str(), &db, SQLITE_OPEN_READONLY |
SQLITE_OPEN_PRIVATECACHE, 0))
SQLOK(prepare_v2(db,
"SELECT name, posX, posY, posZ FROM player",
-1, &stmt_get_player_pos, NULL))
while ((result = sqlite3_step(stmt_get_player_pos)) != SQLITE_DONE) {
if (result == SQLITE_BUSY) { // Wait some time and try again
usleep(10000);
} else if (result != SQLITE_ROW) {
throw std::runtime_error(sqlite3_errmsg(db));
}
Player player;
const unsigned char *name_ = sqlite3_column_text(stmt_get_player_pos, 0);
player.name = reinterpret_cast<const char*>(name_);
player.x = sqlite3_column_double(stmt_get_player_pos, 1);
player.y = sqlite3_column_double(stmt_get_player_pos, 2);
player.z = sqlite3_column_double(stmt_get_player_pos, 3);
player.x /= 10.0f;
player.y /= 10.0f;
player.z /= 10.0f;
m_players.push_back(player);
}
sqlite3_finalize(stmt_get_player_pos);
sqlite3_close(db);
}
/**********/
PlayerAttributes::Players::const_iterator PlayerAttributes::begin() const
{
return m_players.cbegin();