Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: duplicate player creation on death screen #3325

Merged
merged 2 commits into from
Feb 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8908,6 +8908,20 @@ bool Player::saySpell(SpeakClasses type, const std::string &text, bool isGhostMo
return true;
}

void Player::setDead(bool isDead) {
m_isDead = isDead;
const auto &thisPlayer = static_self_cast<Player>();
if (isDead) {
g_game().addDeadPlayer(thisPlayer);
} else {
g_game().removeDeadPlayer(getName());
}
}

bool Player::isDead() const {
return m_isDead;
}

void Player::triggerMomentum() {
double_t chance = 0;
if (const auto &item = getInventoryItem(CONST_SLOT_HEAD)) {
Expand Down Expand Up @@ -9963,7 +9977,6 @@ void Player::onRemoveCreature(const std::shared_ptr<Creature> &creature, bool is
guild->removeMember(player);
}

g_game().removePlayerUniqueLogin(player);
loginPosition = getPosition();
lastLogout = time(nullptr);
g_logger().info("{} has logged out", getName());
Expand Down
8 changes: 2 additions & 6 deletions src/creatures/players/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1623,12 +1623,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
uint16_t getLookCorpse() const override;
void getPathSearchParams(const std::shared_ptr<Creature> &creature, FindPathParams &fpp) override;

void setDead(bool isDead) {
m_isDead = isDead;
}
bool isDead() const override {
return m_isDead;
}
void setDead(bool isDead);
bool isDead() const override;

void triggerMomentum();
void clearCooldowns();
Expand Down
63 changes: 20 additions & 43 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10003,6 +10003,23 @@ void Game::updatePlayerSaleItems(uint32_t playerId) {
player->setScheduledSaleUpdate(false);
}

void Game::addDeadPlayer(const std::shared_ptr<Player> &player) {
m_deadPlayers[player->getName()] = player;
}

void Game::removeDeadPlayer(const std::string &playerName) {
m_deadPlayers.erase(playerName);
}

std::shared_ptr<Player> Game::getDeadPlayer(const std::string &playerName) {
auto it = m_deadPlayers.find(playerName);
if (it != m_deadPlayers.end()) {
return it->second.lock();
}

return nullptr;
}

void Game::addPlayer(const std::shared_ptr<Player> &player) {
const std::string &lowercase_name = asLowerCaseString(player->getName());
mappedPlayerNames[lowercase_name] = player;
Expand Down Expand Up @@ -10581,54 +10598,14 @@ bool Game::addItemStoreInbox(const std::shared_ptr<Player> &player, uint32_t ite
return true;
}

void Game::addPlayerUniqueLogin(const std::shared_ptr<Player> &player) {
if (!player) {
g_logger().error("Attempted to add null player to unique player names list");
return;
}

const std::string &lowercase_name = asLowerCaseString(player->getName());
m_uniqueLoginPlayerNames[lowercase_name] = player;
}

std::shared_ptr<Player> Game::getPlayerUniqueLogin(const std::string &playerName) const {
if (playerName.empty()) {
g_logger().error("Attempted to get player with empty name string");
return nullptr;
}

auto it = m_uniqueLoginPlayerNames.find(asLowerCaseString(playerName));
return (it != m_uniqueLoginPlayerNames.end()) ? it->second.lock() : nullptr;
}

void Game::removePlayerUniqueLogin(const std::string &playerName) {
if (playerName.empty()) {
g_logger().error("Attempted to remove player with empty name string from unique player names list");
return;
}

const std::string &lowercase_name = asLowerCaseString(playerName);
m_uniqueLoginPlayerNames.erase(lowercase_name);
}

void Game::removePlayerUniqueLogin(const std::shared_ptr<Player> &player) {
if (!player) {
g_logger().error("Attempted to remove null player from unique player names list.");
return;
}

const std::string &lowercaseName = asLowerCaseString(player->getName());
m_uniqueLoginPlayerNames.erase(lowercaseName);
}

void Game::playerCheckActivity(const std::string &playerName, int interval) {
const auto &player = getPlayerUniqueLogin(playerName);
const auto &player = getPlayerByName(playerName);
if (!player) {
return;
}

if (player->getIP() == 0) {
g_game().removePlayerUniqueLogin(playerName);
g_game().removeDeadPlayer(playerName);
g_logger().info("Player with name '{}' has logged out due to exited in death screen", player->getName());
player->disconnect();
return;
Expand All @@ -10642,8 +10619,8 @@ void Game::playerCheckActivity(const std::string &playerName, int interval) {
player->m_deathTime += interval;
const int32_t kickAfterMinutes = g_configManager().getNumber(KICK_AFTER_MINUTES);
if (player->m_deathTime > (kickAfterMinutes * 60000) + 60000) {
g_game().removeDeadPlayer(playerName);
g_logger().info("Player with name '{}' has logged out due to inactivity after death", player->getName());
g_game().removePlayerUniqueLogin(playerName);
player->disconnect();
return;
}
Expand Down
43 changes: 5 additions & 38 deletions src/game/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,10 @@ class Game {
return npcs;
}

void addDeadPlayer(const std::shared_ptr<Player> &player);
void removeDeadPlayer(const std::string &playerName);
std::shared_ptr<Player> getDeadPlayer(const std::string &playerName);

const std::vector<ItemClassification*> &getItemsClassifications() const {
return itemsClassifications;
}
Expand Down Expand Up @@ -635,43 +639,6 @@ class Game {
void sendUpdateCreature(const std::shared_ptr<Creature> &creature);
std::shared_ptr<Item> wrapItem(const std::shared_ptr<Item> &item, const std::shared_ptr<House> &house);

/**
* @brief Adds a player to the unique login map.
* @details The function registers a player in the unique login map to ensure no duplicate logins.
* If the player pointer is null, it logs an error and returns.
*
* @param player A pointer to the Player object to add.
*/
void addPlayerUniqueLogin(const std::shared_ptr<Player> &player);

/**
* @brief Gets a player from the unique login map using their name.
* @details The function attempts to find a player in the unique login map using their name.
* If the player's name is not found, the function returns a null pointer.
* If an empty string is provided, it logs an error and returns a null pointer.
*
* @param playerName The name of the player to search for.
* @return A pointer to the Player object if found, null otherwise.
*/
std::shared_ptr<Player> getPlayerUniqueLogin(const std::string &playerName) const;

/**
* @brief Removes a player from the unique login map using their name.
* @details The function removes a player from the unique login map using their name.
* If an empty string is provided, it logs an error and returns.
*
* @param playerName The name of the player to remove.
*/
void removePlayerUniqueLogin(const std::string &playerName);

/**
* @brief Removes a player from the unique login map.
* @details The function removes a player from the unique login map.
* If the player pointer is null, it logs an error and returns.
*
* @param player A pointer to the Player object to remove.
*/
void removePlayerUniqueLogin(const std::shared_ptr<Player> &player);
void playerCheckActivity(const std::string &playerName, int interval);

/**
Expand Down Expand Up @@ -824,7 +791,7 @@ class Game {
phmap::flat_hash_map<std::string, QueryHighscoreCacheEntry> queryCache;
phmap::flat_hash_map<std::string, HighscoreCacheEntry> highscoreCache;

phmap::flat_hash_map<std::string, std::weak_ptr<Player>> m_uniqueLoginPlayerNames;
std::unordered_map<std::string, std::weak_ptr<Player>> m_deadPlayers;
phmap::parallel_flat_hash_map<uint32_t, std::shared_ptr<Player>> players;
phmap::flat_hash_map<std::string, std::weak_ptr<Player>> mappedPlayerNames;
phmap::parallel_flat_hash_map<uint32_t, std::shared_ptr<Guild>> guilds;
Expand Down
37 changes: 14 additions & 23 deletions src/server/network/protocol/protocolgame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,34 +488,29 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS
g_logger().debug("Player logging in in version '{}' and oldProtocol '{}'", getVersion(), oldProtocol);

// dispatcher thread
std::shared_ptr<Player> foundPlayer = g_game().getPlayerUniqueLogin(name);
std::shared_ptr<Player> foundPlayer = g_game().getPlayerByName(name);
if (!foundPlayer) {
player = std::make_shared<Player>(getThis());
player->setName(name);
g_game().addPlayerUniqueLogin(player);

player->setID();

if (!IOLoginDataLoad::preLoadPlayer(player, name)) {
g_game().removePlayerUniqueLogin(player);
disconnectClient("Your character could not be loaded.");
return;
}

if (IOBan::isPlayerNamelocked(player->getGUID())) {
g_game().removePlayerUniqueLogin(player);
disconnectClient("Your character has been namelocked.");
return;
}

if (g_game().getGameState() == GAME_STATE_CLOSING && !player->hasFlag(PlayerFlags_t::CanAlwaysLogin)) {
g_game().removePlayerUniqueLogin(player);
disconnectClient("The game is just going down.\nPlease try again later.");
return;
}

if (g_game().getGameState() == GAME_STATE_CLOSED && !player->hasFlag(PlayerFlags_t::CanAlwaysLogin)) {
g_game().removePlayerUniqueLogin(player);
auto maintainMessage = g_configManager().getString(MAINTAIN_MODE_MESSAGE);
if (!maintainMessage.empty()) {
disconnectClient(maintainMessage);
Expand All @@ -526,15 +521,13 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS
}

if (g_configManager().getBoolean(ONLY_PREMIUM_ACCOUNT) && !player->isPremium() && (player->getGroup()->id < GROUP_TYPE_GAMEMASTER || player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER)) {
g_game().removePlayerUniqueLogin(player);
disconnectClient("Your premium time for this account is out.\n\nTo play please buy additional premium time from our website");
return;
}

auto onlineCount = g_game().getPlayersByAccount(player->getAccount()).size();
auto maxOnline = g_configManager().getNumber(MAX_PLAYERS_PER_ACCOUNT);
if (player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER && onlineCount >= maxOnline) {
g_game().removePlayerUniqueLogin(player);
disconnectClient(fmt::format("You may only login with {} character{}\nof your account at the same time.", maxOnline, maxOnline > 1 ? "s" : ""));
return;
}
Expand All @@ -554,7 +547,6 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS
ss << "Your account has been permanently banned by " << banInfo.bannedBy << ".\n\nReason specified:\n"
<< banInfo.reason;
}
g_game().removePlayerUniqueLogin(player);
disconnectClient(ss.str());
return;
}
Expand All @@ -575,12 +567,10 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS
output->addByte(retryTime);
send(output);
disconnect();
g_game().removePlayerUniqueLogin(player);
return;
}

if (!IOLoginData::loadPlayerById(player, player->getGUID(), false)) {
g_game().removePlayerUniqueLogin(player);
disconnectClient("Your character could not be loaded.");
g_logger().warn("Player {} could not be loaded", player->getName());
return;
Expand All @@ -600,14 +590,12 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS
}
}
if (countOutsizePZ >= maxOutsizePZ) {
g_game().removePlayerUniqueLogin(player);
disconnectClient(fmt::format("You can only have {} character{} from your account outside of a protection zone.", maxOutsizePZ == 1 ? "one" : std::to_string(maxOutsizePZ), maxOutsizePZ > 1 ? "s" : ""));
return;
}
}

if (!g_game().placeCreature(player, player->getLoginPosition()) && !g_game().placeCreature(player, player->getTemplePosition(), false, true)) {
g_game().removePlayerUniqueLogin(player);
disconnectClient("Temple position is wrong. Please, contact the administrator.");
g_logger().warn("Player {} temple position is wrong", player->getName());
return;
Expand Down Expand Up @@ -643,7 +631,7 @@ void ProtocolGame::login(const std::string &name, uint32_t accountId, OperatingS
void ProtocolGame::connect(const std::string &playerName, OperatingSystem_t operatingSystem) {
eventConnect = 0;

std::shared_ptr<Player> foundPlayer = g_game().getPlayerUniqueLogin(playerName);
std::shared_ptr<Player> foundPlayer = g_game().getPlayerByName(playerName);
if (!foundPlayer) {
disconnectClient("You are already logged in.");
return;
Expand All @@ -656,7 +644,6 @@ void ProtocolGame::connect(const std::string &playerName, OperatingSystem_t oper
}

player = foundPlayer;
g_game().addPlayerUniqueLogin(player);

g_chat().removeUserFromAllChannels(player);
player->clearModalWindows();
Expand Down Expand Up @@ -793,14 +780,21 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage &msg) {

std::string characterName = msg.getString();

std::shared_ptr<Player> foundPlayer = g_game().getPlayerUniqueLogin(characterName);
const auto &onlinePlayer = g_game().getPlayerByName(characterName);
const auto &foundPlayer = !onlinePlayer ? g_game().getDeadPlayer(characterName) : onlinePlayer;
if (foundPlayer && foundPlayer->client) {
if (foundPlayer->getProtocolVersion() != getVersion() && foundPlayer->isOldProtocol() != oldProtocol) {
disconnectClient(fmt::format("You are already logged in using protocol '{}'. Please log out from the other session to connect here.", foundPlayer->getProtocolVersion()));
if (foundPlayer->isDead()) {
disconnectClient("You are already logged in.");
return;
}

foundPlayer->client->disconnectClient("You are already connected through another client. Please use only one client at a time!");
auto message = fmt::format("You are already connected through another client. Please use only one client at a time!");
if (foundPlayer->getProtocolVersion() != getVersion() && foundPlayer->isOldProtocol() != oldProtocol) {
message = fmt::format("You are already logged in using protocol '{}'. Please log out from the other session to connect here.", foundPlayer->getProtocolVersion());
}

disconnectClient(message);
return;
}

auto timeStamp = msg.get<uint32_t>();
Expand Down Expand Up @@ -949,9 +943,6 @@ void ProtocolGame::parsePacket(NetworkMessage &msg) {
void ProtocolGame::parsePacketDead(uint8_t recvbyte) {
if (recvbyte == 0x14) {
// Remove player from game if click "ok" using otc
if (player && isOTC) {
g_game().removePlayerUniqueLogin(player->getName());
}
disconnect();
return;
}
Expand Down Expand Up @@ -4218,7 +4209,7 @@ void ProtocolGame::sendCyclopediaCharacterBadges() {
msg.addByte(0x00);
msg.addByte(0x01); // ShowAccountInformation, if 0x01 will show IsOnline, IsPremium, character title, badges

const auto loggedPlayer = g_game().getPlayerUniqueLogin(player->getName());
const auto loggedPlayer = g_game().getPlayerByName(player->getName());
msg.addByte(loggedPlayer ? 0x01 : 0x00); // IsOnline
msg.addByte(player->isPremium() ? 0x01 : 0x00); // IsPremium (GOD has always 'Premium')
// Character loyalty title
Expand Down
Loading