Skip to content

Commit

Permalink
Enable account level changing at runtime (#282)
Browse files Browse the repository at this point in the history
* Enable account level change at runtime

* PR feedback
  • Loading branch information
yungcomputerchair committed Jun 24, 2024
1 parent 003186d commit b780f5e
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 4 deletions.
71 changes: 69 additions & 2 deletions src/CustomCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,74 @@ static void helpCommand(std::string full, std::vector<std::string>& args, CNSock
}

static void accessCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {
Chat::sendServerMessage(sock, "Your access level is " + std::to_string(PlayerManager::getPlayer(sock)->accountLevel));
if (args.size() < 2) {
Chat::sendServerMessage(sock, "Usage: /access <id> [new_level]");
Chat::sendServerMessage(sock, "Use . for id to select yourself");
return;
}

char *tmp;

Player* self = PlayerManager::getPlayer(sock);
int selfAccess = self->accountLevel;

Player* player;
if (args[1].compare(".") == 0) {
player = self;
} else {
int id = std::strtol(args[1].c_str(), &tmp, 10);
if (*tmp) {
Chat::sendServerMessage(sock, "Invalid player ID " + args[1]);
return;
}

player = PlayerManager::getPlayerFromID(id);
if (player == nullptr) {
Chat::sendServerMessage(sock, "Could not find player with ID " + std::to_string(id));
return;
}

// Messing with other players requires a baseline access of 30
if (player != self && selfAccess > 30) {
Chat::sendServerMessage(sock, "Can't check or change other players access levels (insufficient privileges)");
return;
}
}

std::string playerName = PlayerManager::getPlayerName(player);
int currentAccess = player->accountLevel;
if (args.size() < 3) {
// just check
Chat::sendServerMessage(sock, playerName + " has access level " + std::to_string(currentAccess));
return;
}

// Can't change the access level of someone with stronger privileges
// N.B. lower value = stronger privileges
if (currentAccess <= selfAccess) {
Chat::sendServerMessage(sock, "Can't change this player's access level (insufficient privileges)");
return;
}

int newAccess = std::strtol(args[2].c_str(), &tmp, 10);
if (*tmp) {
Chat::sendServerMessage(sock, "Invalid access level " + args[2]);
return;
}

// Can only assign an access level weaker than yours
if (newAccess <= selfAccess) {
Chat::sendServerMessage(sock, "Can only assign privileges weaker than your own");
return;
}

player->accountLevel = newAccess;

// Save to database
int accountId = Database::getAccountIdForPlayer(player->iID);
Database::updateAccountLevel(accountId, newAccess);

Chat::sendServerMessage(sock, "Changed access level for " + playerName + " from " + std::to_string(currentAccess) + " to " + std::to_string(newAccess));
}

static void populationCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {
Expand Down Expand Up @@ -1200,7 +1267,7 @@ static void registerCommand(std::string cmd, int requiredLevel, CommandHandler h

void CustomCommands::init() {
registerCommand("help", 100, helpCommand, "list all unlocked server-side commands");
registerCommand("access", 100, accessCommand, "print your access level");
registerCommand("access", 100, accessCommand, "check or change access levels");
registerCommand("instance", 30, instanceCommand, "print or change your current instance");
registerCommand("mss", 30, mssCommand, "edit Monkey Skyway routes");
registerCommand("npcr", 30, npcRotateCommand, "rotate NPCs");
Expand Down
7 changes: 6 additions & 1 deletion src/PlayerManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,12 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {

response.iID = plr->iID;
response.uiSvrTime = getTime();
response.PCLoadData2CL.iUserLevel = plr->accountLevel;

// The only client-side use of the account level is to block
// the sending of GM packets. Since account level can be changed
// at runtime and we validate it serverside, we can leave this at 0.
response.PCLoadData2CL.iUserLevel = 0; // plr->accountLevel;

response.PCLoadData2CL.iHP = plr->HP;
response.PCLoadData2CL.iLevel = plr->level;
response.PCLoadData2CL.iCandy = plr->money;
Expand Down
6 changes: 5 additions & 1 deletion src/db/Database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,13 @@ namespace Database {
void close();

void findAccount(Account* account, std::string login);
// returns ID, 0 if something failed

// return ID, 0 if something failed
int getAccountIdForPlayer(int playerId);
int addAccount(std::string login, std::string password);

void updateAccountLevel(int accountId, int accountLevel);

// interface for the /ban command
bool banPlayer(int playerId, std::string& reason);
bool unbanPlayer(int playerId);
Expand Down
46 changes: 46 additions & 0 deletions src/db/login.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ void Database::findAccount(Account* account, std::string login) {
sqlite3_finalize(stmt);
}

int Database::getAccountIdForPlayer(int playerId) {
std::lock_guard<std::mutex> lock(dbCrit);

const char* sql = R"(
SELECT AccountID
FROM Players
WHERE PlayerID = ?
LIMIT 1;
)";
sqlite3_stmt* stmt;

sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, playerId);

int rc = sqlite3_step(stmt);
if (rc != SQLITE_ROW) {
std::cout << "[WARN] Database: couldn't get account id for player " << playerId << std::endl;
sqlite3_finalize(stmt);
return 0;
}

int accountId = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
return accountId;
}

int Database::addAccount(std::string login, std::string password) {
std::lock_guard<std::mutex> lock(dbCrit);

Expand All @@ -52,6 +78,26 @@ int Database::addAccount(std::string login, std::string password) {
return sqlite3_last_insert_rowid(db);
}

void Database::updateAccountLevel(int accountId, int accountLevel) {
std::lock_guard<std::mutex> lock(dbCrit);

const char* sql = R"(
UPDATE Accounts SET
AccountLevel = ?
WHERE AccountID = ?;
)";
sqlite3_stmt* stmt;

sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, accountLevel);
sqlite3_bind_int(stmt, 2, accountId);

int rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE)
std::cout << "[WARN] Database fail on updateAccountLevel(): " << sqlite3_errmsg(db) << std::endl;
sqlite3_finalize(stmt);
}

void Database::updateSelected(int accountId, int slot) {
std::lock_guard<std::mutex> lock(dbCrit);

Expand Down

0 comments on commit b780f5e

Please sign in to comment.