summaryrefslogtreecommitdiff
path: root/src/database
diff options
context:
space:
mode:
Diffstat (limited to 'src/database')
-rw-r--r--src/database/database-files.cpp2
-rw-r--r--src/database/database-leveldb.cpp210
-rw-r--r--src/database/database-leveldb.h32
-rw-r--r--src/database/database-postgresql.cpp179
-rw-r--r--src/database/database-postgresql.h24
-rw-r--r--src/database/database-sqlite3.cpp2
6 files changed, 444 insertions, 5 deletions
diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp
index d09f1c074..d2b0b1543 100644
--- a/src/database/database-files.cpp
+++ b/src/database/database-files.cpp
@@ -20,11 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <cassert>
#include <json/json.h>
#include "database-files.h"
-#include "content_sao.h"
#include "remoteplayer.h"
#include "settings.h"
#include "porting.h"
#include "filesys.h"
+#include "server/player_sao.h"
#include "util/string.h"
// !!! WARNING !!!
diff --git a/src/database/database-leveldb.cpp b/src/database/database-leveldb.cpp
index 4a4904c6a..1976ae13d 100644
--- a/src/database/database-leveldb.cpp
+++ b/src/database/database-leveldb.cpp
@@ -26,6 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h"
#include "filesys.h"
#include "exceptions.h"
+#include "remoteplayer.h"
+#include "server/player_sao.h"
+#include "util/serialize.h"
#include "util/string.h"
#include "leveldb/db.h"
@@ -97,5 +100,210 @@ void Database_LevelDB::listAllLoadableBlocks(std::vector<v3s16> &dst)
delete it;
}
-#endif // USE_LEVELDB
+PlayerDatabaseLevelDB::PlayerDatabaseLevelDB(const std::string &savedir)
+{
+ leveldb::Options options;
+ options.create_if_missing = true;
+ leveldb::Status status = leveldb::DB::Open(options,
+ savedir + DIR_DELIM + "players.db", &m_database);
+ ENSURE_STATUS_OK(status);
+}
+
+PlayerDatabaseLevelDB::~PlayerDatabaseLevelDB()
+{
+ delete m_database;
+}
+
+void PlayerDatabaseLevelDB::savePlayer(RemotePlayer *player)
+{
+ /*
+ u8 version = 1
+ u16 hp
+ v3f position
+ f32 pitch
+ f32 yaw
+ u16 breath
+ u32 attribute_count
+ for each attribute {
+ std::string name
+ std::string (long) value
+ }
+ std::string (long) serialized_inventory
+ */
+
+ std::ostringstream os;
+ writeU8(os, 1);
+
+ PlayerSAO *sao = player->getPlayerSAO();
+ sanity_check(sao);
+ writeU16(os, sao->getHP());
+ writeV3F32(os, sao->getBasePosition());
+ writeF32(os, sao->getLookPitch());
+ writeF32(os, sao->getRotation().Y);
+ writeU16(os, sao->getBreath());
+
+ StringMap stringvars = sao->getMeta().getStrings();
+ writeU32(os, stringvars.size());
+ for (const auto &it : stringvars) {
+ os << serializeString(it.first);
+ os << serializeLongString(it.second);
+ }
+ player->inventory.serialize(os);
+
+ leveldb::Status status = m_database->Put(leveldb::WriteOptions(),
+ player->getName(), os.str());
+ ENSURE_STATUS_OK(status);
+ player->onSuccessfulSave();
+}
+
+bool PlayerDatabaseLevelDB::removePlayer(const std::string &name)
+{
+ leveldb::Status s = m_database->Delete(leveldb::WriteOptions(), name);
+ return s.ok();
+}
+
+bool PlayerDatabaseLevelDB::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
+{
+ std::string raw;
+ leveldb::Status s = m_database->Get(leveldb::ReadOptions(),
+ player->getName(), &raw);
+ if (!s.ok())
+ return false;
+ std::istringstream is(raw);
+
+ if (readU8(is) > 1)
+ return false;
+
+ sao->setHPRaw(readU16(is));
+ sao->setBasePosition(readV3F32(is));
+ sao->setLookPitch(readF32(is));
+ sao->setPlayerYaw(readF32(is));
+ sao->setBreath(readU16(is), false);
+
+ u32 attribute_count = readU32(is);
+ for (u32 i = 0; i < attribute_count; i++) {
+ std::string name = deSerializeString(is);
+ std::string value = deSerializeLongString(is);
+ sao->getMeta().setString(name, value);
+ }
+ sao->getMeta().setModified(false);
+
+ // This should always be last.
+ try {
+ player->inventory.deSerialize(is);
+ } catch (SerializationError &e) {
+ errorstream << "Failed to deserialize player inventory. player_name="
+ << player->getName() << " " << e.what() << std::endl;
+ }
+
+ return true;
+}
+
+void PlayerDatabaseLevelDB::listPlayers(std::vector<std::string> &res)
+{
+ leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions());
+ res.clear();
+ for (it->SeekToFirst(); it->Valid(); it->Next()) {
+ res.push_back(it->key().ToString());
+ }
+ delete it;
+}
+
+AuthDatabaseLevelDB::AuthDatabaseLevelDB(const std::string &savedir)
+{
+ leveldb::Options options;
+ options.create_if_missing = true;
+ leveldb::Status status = leveldb::DB::Open(options,
+ savedir + DIR_DELIM + "auth.db", &m_database);
+ ENSURE_STATUS_OK(status);
+}
+
+AuthDatabaseLevelDB::~AuthDatabaseLevelDB()
+{
+ delete m_database;
+}
+
+bool AuthDatabaseLevelDB::getAuth(const std::string &name, AuthEntry &res)
+{
+ std::string raw;
+ leveldb::Status s = m_database->Get(leveldb::ReadOptions(), name, &raw);
+ if (!s.ok())
+ return false;
+ std::istringstream is(raw);
+
+ /*
+ u8 version = 1
+ std::string password
+ u16 number of privileges
+ for each privilege {
+ std::string privilege
+ }
+ s64 last_login
+ */
+
+ if (readU8(is) > 1)
+ return false;
+
+ res.id = 1;
+ res.name = name;
+ res.password = deSerializeString(is);
+
+ u16 privilege_count = readU16(is);
+ res.privileges.clear();
+ res.privileges.reserve(privilege_count);
+ for (u16 i = 0; i < privilege_count; i++) {
+ res.privileges.push_back(deSerializeString(is));
+ }
+
+ res.last_login = readS64(is);
+ return true;
+}
+
+bool AuthDatabaseLevelDB::saveAuth(const AuthEntry &authEntry)
+{
+ std::ostringstream os;
+ writeU8(os, 1);
+ os << serializeString(authEntry.password);
+
+ size_t privilege_count = authEntry.privileges.size();
+ FATAL_ERROR_IF(privilege_count > U16_MAX,
+ "Unsupported number of privileges");
+ writeU16(os, privilege_count);
+ for (const std::string &privilege : authEntry.privileges) {
+ os << serializeString(privilege);
+ }
+
+ writeS64(os, authEntry.last_login);
+ leveldb::Status s = m_database->Put(leveldb::WriteOptions(),
+ authEntry.name, os.str());
+ return s.ok();
+}
+
+bool AuthDatabaseLevelDB::createAuth(AuthEntry &authEntry)
+{
+ return saveAuth(authEntry);
+}
+
+bool AuthDatabaseLevelDB::deleteAuth(const std::string &name)
+{
+ leveldb::Status s = m_database->Delete(leveldb::WriteOptions(), name);
+ return s.ok();
+}
+
+void AuthDatabaseLevelDB::listNames(std::vector<std::string> &res)
+{
+ leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions());
+ res.clear();
+ for (it->SeekToFirst(); it->Valid(); it->Next()) {
+ res.emplace_back(it->key().ToString());
+ }
+ delete it;
+}
+
+void AuthDatabaseLevelDB::reload()
+{
+ // No-op for LevelDB.
+}
+
+#endif // USE_LEVELDB
diff --git a/src/database/database-leveldb.h b/src/database/database-leveldb.h
index d30f9f8f5..61def1256 100644
--- a/src/database/database-leveldb.h
+++ b/src/database/database-leveldb.h
@@ -45,4 +45,36 @@ private:
leveldb::DB *m_database;
};
+class PlayerDatabaseLevelDB : public PlayerDatabase
+{
+public:
+ PlayerDatabaseLevelDB(const std::string &savedir);
+ ~PlayerDatabaseLevelDB();
+
+ void savePlayer(RemotePlayer *player);
+ bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
+ bool removePlayer(const std::string &name);
+ void listPlayers(std::vector<std::string> &res);
+
+private:
+ leveldb::DB *m_database;
+};
+
+class AuthDatabaseLevelDB : public AuthDatabase
+{
+public:
+ AuthDatabaseLevelDB(const std::string &savedir);
+ virtual ~AuthDatabaseLevelDB();
+
+ virtual bool getAuth(const std::string &name, AuthEntry &res);
+ virtual bool saveAuth(const AuthEntry &authEntry);
+ virtual bool createAuth(AuthEntry &authEntry);
+ virtual bool deleteAuth(const std::string &name);
+ virtual void listNames(std::vector<std::string> &res);
+ virtual void reload();
+
+private:
+ leveldb::DB *m_database;
+};
+
#endif // USE_LEVELDB
diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp
index d7c94ff15..e1bb39928 100644
--- a/src/database/database-postgresql.cpp
+++ b/src/database/database-postgresql.cpp
@@ -36,8 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "debug.h"
#include "exceptions.h"
#include "settings.h"
-#include "content_sao.h"
#include "remoteplayer.h"
+#include "server/player_sao.h"
Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) :
m_connect_string(connect_string)
@@ -160,6 +160,11 @@ void Database_PostgreSQL::endSave()
checkResults(PQexec(m_conn, "COMMIT;"));
}
+void Database_PostgreSQL::rollback()
+{
+ checkResults(PQexec(m_conn, "ROLLBACK;"));
+}
+
MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string):
Database_PostgreSQL(connect_string),
MapDatabase()
@@ -301,7 +306,7 @@ void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst)
int numrows = PQntuples(results);
for (int row = 0; row < numrows; ++row)
- dst.push_back(pg_to_v3s16(results, 0, 0));
+ dst.push_back(pg_to_v3s16(results, row, 0));
PQclear(results);
}
@@ -631,4 +636,174 @@ void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res)
PQclear(results);
}
+AuthDatabasePostgreSQL::AuthDatabasePostgreSQL(const std::string &connect_string) :
+ Database_PostgreSQL(connect_string), AuthDatabase()
+{
+ connectToDatabase();
+}
+
+void AuthDatabasePostgreSQL::createDatabase()
+{
+ createTableIfNotExists("auth",
+ "CREATE TABLE auth ("
+ "id SERIAL,"
+ "name TEXT UNIQUE,"
+ "password TEXT,"
+ "last_login INT NOT NULL DEFAULT 0,"
+ "PRIMARY KEY (id)"
+ ");");
+
+ createTableIfNotExists("user_privileges",
+ "CREATE TABLE user_privileges ("
+ "id INT,"
+ "privilege TEXT,"
+ "PRIMARY KEY (id, privilege),"
+ "CONSTRAINT fk_id FOREIGN KEY (id) REFERENCES auth (id) ON DELETE CASCADE"
+ ");");
+}
+
+void AuthDatabasePostgreSQL::initStatements()
+{
+ prepareStatement("auth_read", "SELECT id, name, password, last_login FROM auth WHERE name = $1");
+ prepareStatement("auth_write", "UPDATE auth SET name = $1, password = $2, last_login = $3 WHERE id = $4");
+ prepareStatement("auth_create", "INSERT INTO auth (name, password, last_login) VALUES ($1, $2, $3) RETURNING id");
+ prepareStatement("auth_delete", "DELETE FROM auth WHERE name = $1");
+
+ prepareStatement("auth_list_names", "SELECT name FROM auth ORDER BY name DESC");
+
+ prepareStatement("auth_read_privs", "SELECT privilege FROM user_privileges WHERE id = $1");
+ prepareStatement("auth_write_privs", "INSERT INTO user_privileges (id, privilege) VALUES ($1, $2)");
+ prepareStatement("auth_delete_privs", "DELETE FROM user_privileges WHERE id = $1");
+}
+
+bool AuthDatabasePostgreSQL::getAuth(const std::string &name, AuthEntry &res)
+{
+ verifyDatabase();
+
+ const char *values[] = { name.c_str() };
+ PGresult *result = execPrepared("auth_read", 1, values, false, false);
+ int numrows = PQntuples(result);
+ if (numrows == 0) {
+ PQclear(result);
+ return false;
+ }
+
+ res.id = pg_to_uint(result, 0, 0);
+ res.name = std::string(PQgetvalue(result, 0, 1), PQgetlength(result, 0, 1));
+ res.password = std::string(PQgetvalue(result, 0, 2), PQgetlength(result, 0, 2));
+ res.last_login = pg_to_int(result, 0, 3);
+
+ PQclear(result);
+
+ std::string playerIdStr = itos(res.id);
+ const char *privsValues[] = { playerIdStr.c_str() };
+ PGresult *results = execPrepared("auth_read_privs", 1, privsValues, false);
+
+ numrows = PQntuples(results);
+ for (int row = 0; row < numrows; row++)
+ res.privileges.emplace_back(PQgetvalue(results, row, 0));
+
+ PQclear(results);
+
+ return true;
+}
+
+bool AuthDatabasePostgreSQL::saveAuth(const AuthEntry &authEntry)
+{
+ verifyDatabase();
+
+ beginSave();
+
+ std::string lastLoginStr = itos(authEntry.last_login);
+ std::string idStr = itos(authEntry.id);
+ const char *values[] = {
+ authEntry.name.c_str() ,
+ authEntry.password.c_str(),
+ lastLoginStr.c_str(),
+ idStr.c_str(),
+ };
+ execPrepared("auth_write", 4, values);
+
+ writePrivileges(authEntry);
+
+ endSave();
+ return true;
+}
+
+bool AuthDatabasePostgreSQL::createAuth(AuthEntry &authEntry)
+{
+ verifyDatabase();
+
+ std::string lastLoginStr = itos(authEntry.last_login);
+ const char *values[] = {
+ authEntry.name.c_str() ,
+ authEntry.password.c_str(),
+ lastLoginStr.c_str()
+ };
+
+ beginSave();
+
+ PGresult *result = execPrepared("auth_create", 3, values, false, false);
+
+ int numrows = PQntuples(result);
+ if (numrows == 0) {
+ errorstream << "Strange behaviour on auth creation, no ID returned." << std::endl;
+ PQclear(result);
+ rollback();
+ return false;
+ }
+
+ authEntry.id = pg_to_uint(result, 0, 0);
+ PQclear(result);
+
+ writePrivileges(authEntry);
+
+ endSave();
+ return true;
+}
+
+bool AuthDatabasePostgreSQL::deleteAuth(const std::string &name)
+{
+ verifyDatabase();
+
+ const char *values[] = { name.c_str() };
+ execPrepared("auth_delete", 1, values);
+
+ // privileges deleted by foreign key on delete cascade
+ return true;
+}
+
+void AuthDatabasePostgreSQL::listNames(std::vector<std::string> &res)
+{
+ verifyDatabase();
+
+ PGresult *results = execPrepared("auth_list_names", 0,
+ NULL, NULL, NULL, false, false);
+
+ int numrows = PQntuples(results);
+
+ for (int row = 0; row < numrows; ++row)
+ res.emplace_back(PQgetvalue(results, row, 0));
+
+ PQclear(results);
+}
+
+void AuthDatabasePostgreSQL::reload()
+{
+ // noop for PgSQL
+}
+
+void AuthDatabasePostgreSQL::writePrivileges(const AuthEntry &authEntry)
+{
+ std::string authIdStr = itos(authEntry.id);
+ const char *values[] = { authIdStr.c_str() };
+ execPrepared("auth_delete_privs", 1, values);
+
+ for (const std::string &privilege : authEntry.privileges) {
+ const char *values[] = { authIdStr.c_str(), privilege.c_str() };
+ execPrepared("auth_write_privs", 2, values);
+ }
+}
+
+
#endif // USE_POSTGRESQL
diff --git a/src/database/database-postgresql.h b/src/database/database-postgresql.h
index db0b505c9..f47deda33 100644
--- a/src/database/database-postgresql.h
+++ b/src/database/database-postgresql.h
@@ -34,6 +34,7 @@ public:
void beginSave();
void endSave();
+ void rollback();
bool initialized() const;
@@ -144,3 +145,26 @@ protected:
private:
bool playerDataExists(const std::string &playername);
};
+
+class AuthDatabasePostgreSQL : private Database_PostgreSQL, public AuthDatabase
+{
+public:
+ AuthDatabasePostgreSQL(const std::string &connect_string);
+ virtual ~AuthDatabasePostgreSQL() = default;
+
+ virtual void verifyDatabase() { Database_PostgreSQL::verifyDatabase(); }
+
+ virtual bool getAuth(const std::string &name, AuthEntry &res);
+ virtual bool saveAuth(const AuthEntry &authEntry);
+ virtual bool createAuth(AuthEntry &authEntry);
+ virtual bool deleteAuth(const std::string &name);
+ virtual void listNames(std::vector<std::string> &res);
+ virtual void reload();
+
+protected:
+ virtual void createDatabase();
+ virtual void initStatements();
+
+private:
+ virtual void writePrivileges(const AuthEntry &authEntry);
+};
diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp
index 1bacdfe6c..4560743b9 100644
--- a/src/database/database-sqlite3.cpp
+++ b/src/database/database-sqlite3.cpp
@@ -33,8 +33,8 @@ SQLite format specification:
#include "settings.h"
#include "porting.h"
#include "util/string.h"
-#include "content_sao.h"
#include "remoteplayer.h"
+#include "server/player_sao.h"
#include <cassert>