OpenFusion/src/db/shard.cpp

332 lines
9.1 KiB
C++

#include "db/internal.hpp"
#include <assert.h>
// Miscellanious in-game database interactions
static int getAccountIDFromPlayerID(int playerId, int *accountLevel=nullptr) {
const char *sql = R"(
SELECT Players.AccountID, AccountLevel
FROM Players
JOIN Accounts ON Players.AccountID = Accounts.AccountID
WHERE PlayerID = ?;
)";
sqlite3_stmt *stmt;
// get AccountID from PlayerID
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerId);
if (sqlite3_step(stmt) != SQLITE_ROW) {
std::cout << "[WARN] Database: failed to get AccountID from PlayerID: " << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(stmt);
return -1;
}
int accountId = sqlite3_column_int(stmt, 0);
// optional secondary return value, for checking GM status
if (accountLevel != nullptr)
*accountLevel = sqlite3_column_int(stmt, 1);
sqlite3_finalize(stmt);
return accountId;
}
static bool banAccount(int accountId, int days, std::string& reason) {
const char* sql = R"(
UPDATE Accounts SET
BannedSince = (strftime('%s', 'now')),
BannedUntil = (strftime('%s', 'now')) + ?,
BanReason = ?
WHERE AccountID = ?;
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, days * 86400); // convert days to seconds
sqlite3_bind_text(stmt, 2, reason.c_str(), -1, NULL);
sqlite3_bind_int(stmt, 3, accountId);
if (sqlite3_step(stmt) != SQLITE_DONE) {
std::cout << "[WARN] Database: failed to ban account: " << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(stmt);
return false;
}
sqlite3_finalize(stmt);
return true;
}
static bool unbanAccount(int accountId) {
const char* sql = R"(
UPDATE Accounts SET
BannedSince = 0,
BannedUntil = 0,
BanReason = ''
WHERE AccountID = ?;
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, accountId);
if (sqlite3_step(stmt) != SQLITE_DONE) {
std::cout << "[WARN] Database: failed to unban account: " << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(stmt);
return false;
}
sqlite3_finalize(stmt);
return true;
}
// expressed in days
#define THIRTY_YEARS 10957
bool Database::banPlayer(int playerId, std::string& reason) {
std::lock_guard<std::mutex> lock(dbCrit);
int accountLevel;
int accountId = getAccountIDFromPlayerID(playerId, &accountLevel);
if (accountId < 0) {
return false;
}
if (accountLevel <= 30) {
std::cout << "[WARN] Cannot ban a GM." << std::endl;
return false;
}
// do the ban
if (!banAccount(accountId, THIRTY_YEARS, reason)) {
return false;
}
return true;
}
bool Database::unbanPlayer(int playerId) {
std::lock_guard<std::mutex> lock(dbCrit);
int accountId = getAccountIDFromPlayerID(playerId);
if (accountId < 0)
return false;
return unbanAccount(accountId);
}
// buddies
// returns num of buddies + blocked players
int Database::getNumBuddies(Player* player) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
SELECT COUNT(*)
FROM Buddyships
WHERE PlayerAID = ? OR PlayerBID = ?;
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, player->iID);
sqlite3_bind_int(stmt, 2, player->iID);
sqlite3_step(stmt);
int result = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
sql = R"(
SELECT COUNT(*)
FROM Blocks
WHERE PlayerID = ?;
)";
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, player->iID);
sqlite3_step(stmt);
result += sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
// again, for peace of mind
return result > 50 ? 50 : result;
}
void Database::addBuddyship(int playerA, int playerB) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
INSERT INTO Buddyships (PlayerAID, PlayerBID)
VALUES (?, ?);
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerA);
sqlite3_bind_int(stmt, 2, playerB);
if (sqlite3_step(stmt) != SQLITE_DONE)
std::cout << "[WARN] Database: failed to add buddyship: " << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(stmt);
}
void Database::removeBuddyship(int playerA, int playerB) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
DELETE FROM Buddyships
WHERE (PlayerAID = ? AND PlayerBID = ?) OR (PlayerAID = ? AND PlayerBID = ?);
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerA);
sqlite3_bind_int(stmt, 2, playerB);
sqlite3_bind_int(stmt, 3, playerB);
sqlite3_bind_int(stmt, 4, playerA);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
// blocking
void Database::addBlock(int playerId, int blockedPlayerId) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
INSERT INTO Blocks (PlayerID, BlockedPlayerID)
VALUES (?, ?);
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerId);
sqlite3_bind_int(stmt, 2, blockedPlayerId);
if (sqlite3_step(stmt) != SQLITE_DONE)
std::cout << "[WARN] Database: failed to block player: " << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(stmt);
}
void Database::removeBlock(int playerId, int blockedPlayerId) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
DELETE FROM Blocks
WHERE PlayerID = ? AND BlockedPlayerID = ?;
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerId);
sqlite3_bind_int(stmt, 2, blockedPlayerId);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
}
RaceRanking Database::getTopRaceRanking(int epID, int playerID) {
std::lock_guard<std::mutex> lock(dbCrit);
std::string sql(R"(
SELECT
EPID, PlayerID, Score, RingCount, Time, Timestamp
FROM RaceResults
WHERE EPID = ?
)");
if (playerID > -1)
sql += " AND PlayerID = ? ";
sql += R"(
ORDER BY Score DESC
LIMIT 1;
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, epID);
if(playerID > -1)
sqlite3_bind_int(stmt, 2, playerID);
RaceRanking ranking = {};
if (sqlite3_step(stmt) != SQLITE_ROW) {
// this race hasn't been run before, so return a blank ranking
sqlite3_finalize(stmt);
return ranking;
}
assert(epID == sqlite3_column_int(stmt, 0)); // EPIDs should always match
ranking.EPID = epID;
ranking.PlayerID = sqlite3_column_int(stmt, 1);
ranking.Score = sqlite3_column_int(stmt, 2);
ranking.RingCount = sqlite3_column_int(stmt, 3);
ranking.Time = sqlite3_column_int64(stmt, 4);
ranking.Timestamp = sqlite3_column_int64(stmt, 5);
sqlite3_finalize(stmt);
return ranking;
}
void Database::postRaceRanking(RaceRanking ranking) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
INSERT INTO RaceResults
(EPID, PlayerID, Score, RingCount, Time, Timestamp)
VALUES(?, ?, ?, ?, ?, ?);
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, ranking.EPID);
sqlite3_bind_int(stmt, 2, ranking.PlayerID);
sqlite3_bind_int(stmt, 3, ranking.Score);
sqlite3_bind_int(stmt, 4, ranking.RingCount);
sqlite3_bind_int64(stmt, 5, ranking.Time);
sqlite3_bind_int64(stmt, 6, ranking.Timestamp);
if (sqlite3_step(stmt) != SQLITE_DONE) {
std::cout << "[WARN] Database: Failed to post race result" << std::endl;
}
sqlite3_finalize(stmt);
}
bool Database::isCodeRedeemed(int playerId, std::string code) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
SELECT COUNT(*)
FROM RedeemedCodes
WHERE PlayerID = ? AND Code = ?
LIMIT 1;
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerId);
sqlite3_bind_text(stmt, 2, code.c_str(), -1, NULL);
sqlite3_step(stmt);
int result = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
return result;
}
void Database::recordCodeRedemption(int playerId, std::string code) {
std::lock_guard<std::mutex> lock(dbCrit);
const char* sql = R"(
INSERT INTO RedeemedCodes (PlayerID, Code)
VALUES (?, ?);
)";
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerId);
sqlite3_bind_text(stmt, 2, code.c_str(), -1, NULL);
if (sqlite3_step(stmt) != SQLITE_DONE)
std::cout << "[WARN] Database: recording of code redemption failed: " << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(stmt);
}