mirror of
				https://github.com/OpenFusionProject/OpenFusion.git
				synced 2025-11-04 02:30:21 +00:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			575754f727
			...
			02c1c681dd
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 02c1c681dd | |||
| 
						 | 
					225515ec21 | ||
| 
						 | 
					f436108d24 | ||
| 
						 | 
					47dbc6d35e | ||
| 
						 | 
					b780f5ee60 | ||
| 
						 | 
					003186d97a | 
@@ -83,7 +83,77 @@ 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) {
 | 
					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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string msg = "Changed access level for " + playerName + " from " + std::to_string(currentAccess) + " to " + std::to_string(newAccess);
 | 
				
			||||||
 | 
					    if (newAccess <= 50 && currentAccess > 50)
 | 
				
			||||||
 | 
					        msg += " (they must log out and back in for some commands to be enabled)";
 | 
				
			||||||
 | 
					    Chat::sendServerMessage(sock, msg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void populationCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {
 | 
					static void populationCommand(std::string full, std::vector<std::string>& args, CNSocket* sock) {
 | 
				
			||||||
@@ -1200,7 +1270,7 @@ static void registerCommand(std::string cmd, int requiredLevel, CommandHandler h
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void CustomCommands::init() {
 | 
					void CustomCommands::init() {
 | 
				
			||||||
    registerCommand("help", 100, helpCommand, "list all unlocked server-side commands");
 | 
					    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("instance", 30, instanceCommand, "print or change your current instance");
 | 
				
			||||||
    registerCommand("mss", 30, mssCommand, "edit Monkey Skyway routes");
 | 
					    registerCommand("mss", 30, mssCommand, "edit Monkey Skyway routes");
 | 
				
			||||||
    registerCommand("npcr", 30, npcRotateCommand, "rotate NPCs");
 | 
					    registerCommand("npcr", 30, npcRotateCommand, "rotate NPCs");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -325,6 +325,13 @@ static void emailSend(CNSocket* sock, CNPacketData* data) {
 | 
				
			|||||||
    std::string logEmail = "[Email] " + PlayerManager::getPlayerName(plr, true) + " (to " + PlayerManager::getPlayerName(&otherPlr, true) + "): <" + email.SubjectLine + ">\n" + email.MsgBody;
 | 
					    std::string logEmail = "[Email] " + PlayerManager::getPlayerName(plr, true) + " (to " + PlayerManager::getPlayerName(&otherPlr, true) + "): <" + email.SubjectLine + ">\n" + email.MsgBody;
 | 
				
			||||||
    std::cout << logEmail << std::endl;
 | 
					    std::cout << logEmail << std::endl;
 | 
				
			||||||
    dump.push_back(logEmail);
 | 
					    dump.push_back(logEmail);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // notification to recipient if online
 | 
				
			||||||
 | 
					    CNSocket* recipient = PlayerManager::getSockFromID(pkt->iTo_PCUID);
 | 
				
			||||||
 | 
					    if (recipient != nullptr)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        emailUpdateCheck(recipient, nullptr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Email::init() {
 | 
					void Email::init() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -155,7 +155,7 @@ void PlayerManager::sendPlayerTo(CNSocket* sock, int X, int Y, int Z) {
 | 
				
			|||||||
 * Nanos the player hasn't unlocked will (and should) be greyed out. Thus, all nanos should be accounted
 | 
					 * Nanos the player hasn't unlocked will (and should) be greyed out. Thus, all nanos should be accounted
 | 
				
			||||||
 * for in these packets, even if the player hasn't unlocked them.
 | 
					 * for in these packets, even if the player hasn't unlocked them.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void sendNanoBookSubset(CNSocket *sock, Player *plr) {
 | 
					static void sendNanoBook(CNSocket *sock, Player *plr, bool resizeOnly) {
 | 
				
			||||||
#ifdef ACADEMY
 | 
					#ifdef ACADEMY
 | 
				
			||||||
    int16_t id = 0;
 | 
					    int16_t id = 0;
 | 
				
			||||||
    INITSTRUCT(sP_FE2CL_REP_NANO_BOOK_SUBSET, pkt);
 | 
					    INITSTRUCT(sP_FE2CL_REP_NANO_BOOK_SUBSET, pkt);
 | 
				
			||||||
@@ -163,6 +163,13 @@ static void sendNanoBookSubset(CNSocket *sock, Player *plr) {
 | 
				
			|||||||
    pkt.PCUID = plr->iID;
 | 
					    pkt.PCUID = plr->iID;
 | 
				
			||||||
    pkt.bookSize = NANO_COUNT;
 | 
					    pkt.bookSize = NANO_COUNT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (resizeOnly) {
 | 
				
			||||||
 | 
					        // triggers nano array resizing without
 | 
				
			||||||
 | 
					        // actually sending nanos
 | 
				
			||||||
 | 
					        sock->sendPacket(pkt, P_FE2CL_REP_NANO_BOOK_SUBSET);
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (id < NANO_COUNT) {
 | 
					    while (id < NANO_COUNT) {
 | 
				
			||||||
        pkt.elementOffset = id;
 | 
					        pkt.elementOffset = id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -210,6 +217,7 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    response.iID = plr->iID;
 | 
					    response.iID = plr->iID;
 | 
				
			||||||
    response.uiSvrTime = getTime();
 | 
					    response.uiSvrTime = getTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    response.PCLoadData2CL.iUserLevel = plr->accountLevel;
 | 
					    response.PCLoadData2CL.iUserLevel = plr->accountLevel;
 | 
				
			||||||
    response.PCLoadData2CL.iHP = plr->HP;
 | 
					    response.PCLoadData2CL.iHP = plr->HP;
 | 
				
			||||||
    response.PCLoadData2CL.iLevel = plr->level;
 | 
					    response.PCLoadData2CL.iLevel = plr->level;
 | 
				
			||||||
@@ -292,14 +300,14 @@ static void enterPlayer(CNSocket* sock, CNPacketData* data) {
 | 
				
			|||||||
    sock->setFEKey(lm->FEKey);
 | 
					    sock->setFEKey(lm->FEKey);
 | 
				
			||||||
    sock->setActiveKey(SOCKETKEY_FE); // send all packets using the FE key from now on
 | 
					    sock->setActiveKey(SOCKETKEY_FE); // send all packets using the FE key from now on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Academy builds receive nanos in a separate packet. These need to be sent
 | 
					    // Academy builds receive nanos in a separate packet. An initial one with the size of the
 | 
				
			||||||
    // before P_FE2CL_REP_PC_ENTER_SUCC as well as after
 | 
					    // nano book needs to be sent before PC_ENTER_SUCC so the client can resize its nano arrays,
 | 
				
			||||||
    // due to a race condition in the client :(
 | 
					    // and then proper packets with the nanos included must be sent after, while the game is loading.
 | 
				
			||||||
    sendNanoBookSubset(sock, plr);
 | 
					    sendNanoBook(sock, plr, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sock->sendPacket(response, P_FE2CL_REP_PC_ENTER_SUCC);
 | 
					    sock->sendPacket(response, P_FE2CL_REP_PC_ENTER_SUCC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sendNanoBookSubset(sock, plr);
 | 
					    sendNanoBook(sock, plr, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // transfer ownership of Player object into the shard (still valid in this function though)
 | 
					    // transfer ownership of Player object into the shard (still valid in this function though)
 | 
				
			||||||
    addPlayer(sock, plr);
 | 
					    addPlayer(sock, plr);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -46,9 +46,13 @@ namespace Database {
 | 
				
			|||||||
    void close();
 | 
					    void close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void findAccount(Account* account, std::string login);
 | 
					    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);
 | 
					    int addAccount(std::string login, std::string password);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void updateAccountLevel(int accountId, int accountLevel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // interface for the /ban command
 | 
					    // interface for the /ban command
 | 
				
			||||||
    bool banPlayer(int playerId, std::string& reason);
 | 
					    bool banPlayer(int playerId, std::string& reason);
 | 
				
			||||||
    bool unbanPlayer(int playerId);
 | 
					    bool unbanPlayer(int playerId);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,32 @@ void Database::findAccount(Account* account, std::string login) {
 | 
				
			|||||||
    sqlite3_finalize(stmt);
 | 
					    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) {
 | 
					int Database::addAccount(std::string login, std::string password) {
 | 
				
			||||||
    std::lock_guard<std::mutex> lock(dbCrit);
 | 
					    std::lock_guard<std::mutex> lock(dbCrit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -52,6 +78,26 @@ int Database::addAccount(std::string login, std::string password) {
 | 
				
			|||||||
    return sqlite3_last_insert_rowid(db);
 | 
					    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) {
 | 
					void Database::updateSelected(int accountId, int slot) {
 | 
				
			||||||
    std::lock_guard<std::mutex> lock(dbCrit);
 | 
					    std::lock_guard<std::mutex> lock(dbCrit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user