diff --git a/dChatServer/CMakeLists.txt b/dChatServer/CMakeLists.txt index 9a47803d6..34d58a3a2 100644 --- a/dChatServer/CMakeLists.txt +++ b/dChatServer/CMakeLists.txt @@ -1,4 +1,5 @@ set(DCHATSERVER_SOURCES + "ChatIgnoreList.cpp" "ChatPacketHandler.cpp" "PlayerContainer.cpp" ) diff --git a/dChatServer/ChatIgnoreList.cpp b/dChatServer/ChatIgnoreList.cpp new file mode 100644 index 000000000..94d1ac7a2 --- /dev/null +++ b/dChatServer/ChatIgnoreList.cpp @@ -0,0 +1,173 @@ +#include "ChatIgnoreList.h" +#include "PlayerContainer.h" +#include "eChatInternalMessageType.h" +#include "BitStreamUtils.h" +#include "PacketUtils.h" +#include "Game.h" +#include "Logger.h" +#include "eObjectBits.h" + +#include "Database.h" + +// A note to future readers, The client handles all the actual ignoring logic: +// not allowing teams, rejecting DMs, friends requets etc. +// The only thing not auto-handled is instance activities force joining the team on the server. + +void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) { + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receivingPlayer); + + //portion that will get routed: + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, type); +} + +void ChatIgnoreList::GetIgnoreList(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerId; + inStream.Read(playerId); + + auto* receiver = Game::playerContainer.GetPlayerData(playerId); + if (!receiver) { + LOG("Tried to get ignore list, but player %llu not found in container", playerId); + return; + } + + if (!receiver->ignoredPlayers.empty()) { + LOG_DEBUG("Player %llu already has an ignore list", playerId); + return; + } + + auto ignoreList = Database::Get()->GetIgnoreList(static_cast(playerId)); + if (ignoreList.empty()) { + LOG_DEBUG("Player %llu has no ignores", playerId); + return; + } + + for (auto& ignoredPlayer : ignoreList) { + receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayer.id, ignoredPlayer.name }); + GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::CHARACTER); + GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::PERSISTENT); + } + + CBITSTREAM; + WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::GET_IGNORE); + + bitStream.Write(false); // Probably is Is Free Trial, but we don't care about that + bitStream.Write(0); // literally spacing due to struct alignment + + bitStream.Write(receiver->ignoredPlayers.size()); + for (const auto& ignoredPlayer : receiver->ignoredPlayers) { + bitStream.Write(ignoredPlayer.playerId); + bitStream.Write(LUWString(ignoredPlayer.playerName, 36)); + } + + Game::server->Send(&bitStream, packet->systemAddress, false); +} + +void ChatIgnoreList::AddIgnore(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerId; + inStream.Read(playerId); + + auto* receiver = Game::playerContainer.GetPlayerData(playerId); + if (!receiver) { + LOG("Tried to get ignore list, but player %llu not found in container", playerId); + return; + } + + constexpr int32_t MAX_IGNORES = 32; + if (receiver->ignoredPlayers.size() > MAX_IGNORES) { + LOG_DEBUG("Player %llu has too many ignores", playerId); + return; + } + + inStream.IgnoreBytes(4); // ignore some garbage zeros idk + + LUWString toIgnoreName(33); + inStream.Read(toIgnoreName); + std::string toIgnoreStr = toIgnoreName.GetAsString(); + + CBITSTREAM; + WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::ADD_IGNORE); + + // Check if the player exists + LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY; + if (toIgnoreStr == receiver->playerName || toIgnoreStr.find("[GM]") == 0) { + LOG_DEBUG("Player %llu tried to ignore themselves", playerId); + + bitStream.Write(ChatIgnoreList::AddResponse::GENERAL_ERROR); + } else if (std::count(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), toIgnoreStr) > 0) { + LOG_DEBUG("Player %llu is already ignoring %s", playerId, toIgnoreStr.c_str()); + + bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED); + } else { + // Get the playerId falling back to query if not online + auto* playerData = Game::playerContainer.GetPlayerData(toIgnoreStr); + if (!playerData) { + // Fall back to query + auto player = Database::Get()->GetCharacterInfo(toIgnoreStr); + if (!player || player->name != toIgnoreStr) { + LOG_DEBUG("Player %s not found", toIgnoreStr.c_str()); + } else { + ignoredPlayerId = player->id; + } + } else { + ignoredPlayerId = playerData->playerID; + } + + if (ignoredPlayerId != LWOOBJID_EMPTY) { + Database::Get()->AddIgnore(static_cast(playerId), static_cast(ignoredPlayerId)); + GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER); + GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT); + + receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayerId, toIgnoreStr }); + LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str()); + + bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS); + } else { + bitStream.Write(ChatIgnoreList::AddResponse::PLAYER_NOT_FOUND); + } + } + + LUWString playerNameSend(toIgnoreStr, 33); + bitStream.Write(playerNameSend); + bitStream.Write(ignoredPlayerId); + + Game::server->Send(&bitStream, packet->systemAddress, false); +} + +void ChatIgnoreList::RemoveIgnore(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerId; + inStream.Read(playerId); + + auto* receiver = Game::playerContainer.GetPlayerData(playerId); + if (!receiver) { + LOG("Tried to get ignore list, but player %llu not found in container", playerId); + return; + } + + inStream.IgnoreBytes(4); // ignore some garbage zeros idk + + LUWString removedIgnoreName(33); + inStream.Read(removedIgnoreName); + std::string removedIgnoreStr = removedIgnoreName.GetAsString(); + + auto toRemove = std::remove(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), removedIgnoreStr); + if (toRemove == receiver->ignoredPlayers.end()) { + LOG_DEBUG("Player %llu is not ignoring %s", playerId, removedIgnoreStr.c_str()); + return; + } + + Database::Get()->RemoveIgnore(static_cast(playerId), static_cast(toRemove->playerId)); + receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end()); + + CBITSTREAM; + WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::REMOVE_IGNORE); + + bitStream.Write(0); + LUWString playerNameSend(removedIgnoreStr, 33); + bitStream.Write(playerNameSend); + + Game::server->Send(&bitStream, packet->systemAddress, false); +} diff --git a/dChatServer/ChatIgnoreList.h b/dChatServer/ChatIgnoreList.h new file mode 100644 index 000000000..c713c966b --- /dev/null +++ b/dChatServer/ChatIgnoreList.h @@ -0,0 +1,27 @@ +#ifndef __CHATIGNORELIST__H__ +#define __CHATIGNORELIST__H__ + +struct Packet; + +#include + +namespace ChatIgnoreList { + void GetIgnoreList(Packet* packet); + void AddIgnore(Packet* packet); + void RemoveIgnore(Packet* packet); + + enum class Response : uint8_t { + ADD_IGNORE = 32, + REMOVE_IGNORE = 33, + GET_IGNORE = 34, + }; + + enum class AddResponse : uint8_t { + SUCCESS, + ALREADY_IGNORED, + PLAYER_NOT_FOUND, + GENERAL_ERROR, + }; +}; + +#endif //!__CHATIGNORELIST__H__ diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index 0a3caa8e5..46a54888e 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -19,15 +19,13 @@ #include "eClientMessageType.h" #include "eGameMessageType.h" -extern PlayerContainer playerContainer; - void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { //Get from the packet which player we want to do something with: CINSTREAM_SKIP_HEADER; LWOOBJID playerID = 0; inStream.Read(playerID); - auto player = playerContainer.GetPlayerData(playerID); + auto player = Game::playerContainer.GetPlayerData(playerID); if (!player) return; auto friendsList = Database::Get()->GetFriendsList(playerID); @@ -43,7 +41,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { fd.friendName = friendData.friendName; //Now check if they're online: - auto fr = playerContainer.GetPlayerData(fd.friendID); + auto fr = Game::playerContainer.GetPlayerData(fd.friendID); if (fr) { fd.isOnline = true; @@ -97,7 +95,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { char isBestFriendRequest{}; inStream.Read(isBestFriendRequest); - auto requestor = playerContainer.GetPlayerData(requestorPlayerID); + auto requestor = Game::playerContainer.GetPlayerData(requestorPlayerID); if (!requestor) { LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str()); return; @@ -107,7 +105,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN); return; }; - std::unique_ptr requestee(playerContainer.GetPlayerData(playerName)); + std::unique_ptr requestee(Game::playerContainer.GetPlayerData(playerName)); // Check if player is online first if (isBestFriendRequest && !requestee) { @@ -175,7 +173,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { // Only do updates if there was a change in the bff status. if (oldBestFriendStatus != bestFriendStatus) { - auto maxBestFriends = playerContainer.GetMaxNumberOfBestFriends(); + auto maxBestFriends = Game::playerContainer.GetMaxNumberOfBestFriends(); if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) { if (requestee->countOfBestFriends >= maxBestFriends) { SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); @@ -208,7 +206,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true); } } else { - auto maxFriends = playerContainer.GetMaxNumberOfFriends(); + auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends(); if (requestee->friends.size() >= maxFriends) { SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); } else if (requestor->friends.size() >= maxFriends) { @@ -232,8 +230,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { std::string friendName = PacketUtils::ReadString(0x15, packet, true); //Now to try and find both of these: - auto requestor = playerContainer.GetPlayerData(playerID); - auto requestee = playerContainer.GetPlayerData(friendName); + auto requestor = Game::playerContainer.GetPlayerData(playerID); + auto requestee = Game::playerContainer.GetPlayerData(friendName); if (!requestor || !requestee) return; eAddFriendResponseType serverResponseCode{}; @@ -315,7 +313,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { Database::Get()->RemoveFriend(playerID, friendID); //Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended: - auto goonA = playerContainer.GetPlayerData(playerID); + auto goonA = Game::playerContainer.GetPlayerData(playerID); if (goonA) { // Remove the friend from our list of friends for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) { @@ -328,7 +326,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { SendRemoveFriend(goonA, friendName, true); } - auto goonB = playerContainer.GetPlayerData(friendID); + auto goonB = Game::playerContainer.GetPlayerData(friendID); if (!goonB) return; // Do it again for other person for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) { @@ -339,7 +337,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { } } - std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID)); + std::string goonAName = GeneralUtils::UTF16ToWTF8(Game::playerContainer.GetName(playerID)); SendRemoveFriend(goonB, goonAName, true); } @@ -348,11 +346,11 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) { LWOOBJID playerID = LWOOBJID_EMPTY; inStream.Read(playerID); - auto* sender = playerContainer.GetPlayerData(playerID); + auto* sender = Game::playerContainer.GetPlayerData(playerID); if (sender == nullptr) return; - if (playerContainer.GetIsMuted(sender)) return; + if (Game::playerContainer.GetIsMuted(sender)) return; const auto senderName = std::string(sender->playerName.c_str()); @@ -367,12 +365,12 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) { if (channel != 8) return; - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); if (team == nullptr) return; for (const auto memberId : team->memberIDs) { - auto* otherMember = playerContainer.GetPlayerData(memberId); + auto* otherMember = Game::playerContainer.GetPlayerData(memberId); if (otherMember == nullptr) return; @@ -406,11 +404,11 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { std::string message = PacketUtils::ReadString(0xAA, packet, true, 512); //Get the bois: - auto goonA = playerContainer.GetPlayerData(senderID); - auto goonB = playerContainer.GetPlayerData(receiverName); + auto goonA = Game::playerContainer.GetPlayerData(senderID); + auto goonB = Game::playerContainer.GetPlayerData(receiverName); if (!goonA || !goonB) return; - if (playerContainer.GetIsMuted(goonA)) return; + if (Game::playerContainer.GetIsMuted(goonA)) return; std::string goonAName = goonA->playerName.c_str(); std::string goonBName = goonB->playerName.c_str(); @@ -468,25 +466,25 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) { inStream.Read(playerID); std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true); - auto* player = playerContainer.GetPlayerData(playerID); + auto* player = Game::playerContainer.GetPlayerData(playerID); if (player == nullptr) { return; } - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); if (team == nullptr) { - team = playerContainer.CreateTeam(playerID); + team = Game::playerContainer.CreateTeam(playerID); } - auto* other = playerContainer.GetPlayerData(invitedPlayer); + auto* other = Game::playerContainer.GetPlayerData(invitedPlayer); if (other == nullptr) { return; } - if (playerContainer.GetTeam(other->playerID) != nullptr) { + if (Game::playerContainer.GetTeam(other->playerID) != nullptr) { return; } @@ -519,12 +517,12 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) { return; } - auto* team = playerContainer.GetTeam(leaderID); + auto* team = Game::playerContainer.GetTeam(leaderID); if (team == nullptr) { LOG("Failed to find team for leader (%llu)", leaderID); - team = playerContainer.GetTeam(playerID); + team = Game::playerContainer.GetTeam(playerID); } if (team == nullptr) { @@ -532,7 +530,7 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) { return; } - playerContainer.AddMember(team, playerID); + Game::playerContainer.AddMember(team, playerID); } void ChatPacketHandler::HandleTeamLeave(Packet* packet) { @@ -542,12 +540,12 @@ void ChatPacketHandler::HandleTeamLeave(Packet* packet) { uint32_t size = 0; inStream.Read(size); - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); LOG("(%llu) leaving team", playerID); if (team != nullptr) { - playerContainer.RemoveMember(team, playerID, false, false, true); + Game::playerContainer.RemoveMember(team, playerID, false, false, true); } } @@ -560,24 +558,24 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet) { LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str()); - auto* kicked = playerContainer.GetPlayerData(kickedPlayer); + auto* kicked = Game::playerContainer.GetPlayerData(kickedPlayer); LWOOBJID kickedId = LWOOBJID_EMPTY; if (kicked != nullptr) { kickedId = kicked->playerID; } else { - kickedId = playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer)); + kickedId = Game::playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer)); } if (kickedId == LWOOBJID_EMPTY) return; - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); if (team != nullptr) { if (team->leaderID != playerID || team->leaderID == kickedId) return; - playerContainer.RemoveMember(team, kickedId, false, true, false); + Game::playerContainer.RemoveMember(team, kickedId, false, true, false); } } @@ -590,16 +588,16 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet) { LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str()); - auto* promoted = playerContainer.GetPlayerData(promotedPlayer); + auto* promoted = Game::playerContainer.GetPlayerData(promotedPlayer); if (promoted == nullptr) return; - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); if (team != nullptr) { if (team->leaderID != playerID) return; - playerContainer.PromoteMember(team, promoted->playerID); + Game::playerContainer.PromoteMember(team, promoted->playerID); } } @@ -613,16 +611,16 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet) { char option; inStream.Read(option); - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); if (team != nullptr) { if (team->leaderID != playerID) return; team->lootFlag = option; - playerContainer.TeamStatusUpdate(team); + Game::playerContainer.TeamStatusUpdate(team); - playerContainer.UpdateTeamsOnWorld(team, false); + Game::playerContainer.UpdateTeamsOnWorld(team, false); } } @@ -631,18 +629,18 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) { LWOOBJID playerID = LWOOBJID_EMPTY; inStream.Read(playerID); - auto* team = playerContainer.GetTeam(playerID); - auto* data = playerContainer.GetPlayerData(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); + auto* data = Game::playerContainer.GetPlayerData(playerID); if (team != nullptr && data != nullptr) { if (team->local && data->zoneID.GetMapID() != team->zoneId.GetMapID() && data->zoneID.GetCloneID() != team->zoneId.GetCloneID()) { - playerContainer.RemoveMember(team, playerID, false, false, true, true); + Game::playerContainer.RemoveMember(team, playerID, false, false, true, true); return; } if (team->memberIDs.size() <= 1 && !team->local) { - playerContainer.DisbandTeam(team); + Game::playerContainer.DisbandTeam(team); return; } @@ -653,16 +651,16 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) { ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY); } - playerContainer.TeamStatusUpdate(team); + Game::playerContainer.TeamStatusUpdate(team); const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName); for (const auto memberId : team->memberIDs) { - auto* otherMember = playerContainer.GetPlayerData(memberId); + auto* otherMember = Game::playerContainer.GetPlayerData(memberId); if (memberId == playerID) continue; - const auto memberName = playerContainer.GetName(memberId); + const auto memberName = Game::playerContainer.GetName(memberId); if (otherMember != nullptr) { ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID); @@ -670,7 +668,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) { ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0)); } - playerContainer.UpdateTeamsOnWorld(team, false); + Game::playerContainer.UpdateTeamsOnWorld(team, false); } } diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index b41ad4ec6..fccbdb236 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -19,6 +19,7 @@ #include "eChatMessageType.h" #include "eChatInternalMessageType.h" #include "eWorldMessageType.h" +#include "ChatIgnoreList.h" #include "Game.h" @@ -34,14 +35,12 @@ namespace Game { AssetManager* assetManager = nullptr; bool shouldShutdown = false; std::mt19937 randomEngine; + PlayerContainer playerContainer; } - Logger* SetupLogger(); void HandlePacket(Packet* packet); -PlayerContainer playerContainer; - int main(int argc, char** argv) { constexpr uint32_t chatFramerate = mediumFramerate; constexpr uint32_t chatFrameDelta = mediumFrameDelta; @@ -108,7 +107,7 @@ int main(int argc, char** argv) { Game::randomEngine = std::mt19937(time(0)); - playerContainer.Initialize(); + Game::playerContainer.Initialize(); //Run it until server gets a kill message from Master: auto t = std::chrono::high_resolution_clock::now(); @@ -200,19 +199,19 @@ void HandlePacket(Packet* packet) { if (static_cast(packet->data[1]) == eConnectionType::CHAT_INTERNAL) { switch (static_cast(packet->data[3])) { case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION: - playerContainer.InsertPlayer(packet); + Game::playerContainer.InsertPlayer(packet); break; case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION: - playerContainer.RemovePlayer(packet); + Game::playerContainer.RemovePlayer(packet); break; case eChatInternalMessageType::MUTE_UPDATE: - playerContainer.MuteUpdate(packet); + Game::playerContainer.MuteUpdate(packet); break; case eChatInternalMessageType::CREATE_TEAM: - playerContainer.CreateTeamServer(packet); + Game::playerContainer.CreateTeamServer(packet); break; case eChatInternalMessageType::ANNOUNCEMENT: { @@ -234,7 +233,15 @@ void HandlePacket(Packet* packet) { break; case eChatMessageType::GET_IGNORE_LIST: - LOG("Asked for ignore list, but is unimplemented right now."); + ChatIgnoreList::GetIgnoreList(packet); + break; + + case eChatMessageType::ADD_IGNORE: + ChatIgnoreList::AddIgnore(packet); + break; + + case eChatMessageType::REMOVE_IGNORE: + ChatIgnoreList::RemoveIgnore(packet); break; case eChatMessageType::TEAM_GET_STATUS: diff --git a/dChatServer/PlayerContainer.h b/dChatServer/PlayerContainer.h index d055ed95d..51bff6c1e 100644 --- a/dChatServer/PlayerContainer.h +++ b/dChatServer/PlayerContainer.h @@ -7,12 +7,26 @@ #include "dServer.h" #include +struct IgnoreData { + inline bool operator==(const std::string& other) const noexcept { + return playerName == other; + } + + inline bool operator==(const LWOOBJID& other) const noexcept { + return playerId == other; + } + + LWOOBJID playerId; + std::string playerName; +}; + struct PlayerData { LWOOBJID playerID; std::string playerName; SystemAddress sysAddr; LWOZONEID zoneID; std::vector friends; + std::vector ignoredPlayers; time_t muteExpire; uint8_t countOfBestFriends = 0; }; diff --git a/dCommon/Game.h b/dCommon/Game.h index 79a271ecd..305b4f7a7 100644 --- a/dCommon/Game.h +++ b/dCommon/Game.h @@ -12,6 +12,7 @@ class AssetManager; struct SystemAddress; class EntityManager; class dZoneManager; +class PlayerContainer; namespace Game { extern Logger* logger; @@ -26,4 +27,5 @@ namespace Game { extern bool shouldShutdown; extern EntityManager* entityManager; extern dZoneManager* zoneManager; + extern PlayerContainer playerContainer; } diff --git a/dDatabase/GameDatabase/GameDatabase.h b/dDatabase/GameDatabase/GameDatabase.h index d874a4885..bcd8550ba 100644 --- a/dDatabase/GameDatabase/GameDatabase.h +++ b/dDatabase/GameDatabase/GameDatabase.h @@ -21,6 +21,7 @@ #include "ICharInfo.h" #include "IAccounts.h" #include "IActivityLog.h" +#include "IIgnoreList.h" #include "IAccountsRewardCodes.h" namespace sql { @@ -39,7 +40,7 @@ class GameDatabase : public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports, public IPropertyContents, public IProperty, public IPetNames, public ICharXml, public IMigrationHistory, public IUgc, public IFriends, public ICharInfo, - public IAccounts, public IActivityLog, public IAccountsRewardCodes { + public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList { public: virtual ~GameDatabase() = default; // TODO: These should be made private. diff --git a/dDatabase/GameDatabase/ITables/IIgnoreList.h b/dDatabase/GameDatabase/ITables/IIgnoreList.h new file mode 100644 index 000000000..cf831203c --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IIgnoreList.h @@ -0,0 +1,20 @@ +#ifndef __IIGNORELIST__H__ +#define __IIGNORELIST__H__ + +#include +#include +#include + +class IIgnoreList { +public: + struct Info { + std::string name; + uint32_t id; + }; + + virtual std::vector GetIgnoreList(const uint32_t playerId) = 0; + virtual void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0; + virtual void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0; +}; + +#endif //!__IIGNORELIST__H__ diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h index 7b4e21e49..836ab56c4 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -103,6 +103,9 @@ class MySQLDatabase : public GameDatabase { std::optional GetDonationTotal(const uint32_t activityId) override; std::optional IsPlaykeyActive(const int32_t playkeyId) override; std::vector GetUgcModels(const LWOOBJID& propertyId) override; + void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override; + void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override; + std::vector GetIgnoreList(const uint32_t playerId) override; void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; std::vector GetRewardCodesByAccountID(const uint32_t account_id) override; private: diff --git a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt index c45510b9b..9f0e7baa6 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt +++ b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt @@ -7,6 +7,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES "CharXml.cpp" "CommandLog.cpp" "Friends.cpp" + "IgnoreList.cpp" "Leaderboard.cpp" "Mail.cpp" "MigrationHistory.cpp" diff --git a/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp b/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp new file mode 100644 index 000000000..283df3244 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp @@ -0,0 +1,22 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetIgnoreList(const uint32_t playerId) { + auto result = ExecuteSelect("SELECT ci.name AS name, il.ignored_player_id AS ignore_id FROM ignore_list AS il JOIN charinfo AS ci ON il.ignored_player_id = ci.id WHERE il.player_id = ?", playerId); + + std::vector ignoreList; + + ignoreList.reserve(result->rowsCount()); + while (result->next()) { + ignoreList.push_back(IIgnoreList::Info{ result->getString("name").c_str(), result->getUInt("ignore_id") }); + } + + return ignoreList; +} + +void MySQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) { + ExecuteInsert("INSERT IGNORE INTO ignore_list (player_id, ignored_player_id) VALUES (?, ?)", playerId, ignoredPlayerId); +} + +void MySQLDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) { + ExecuteDelete("DELETE FROM ignore_list WHERE player_id = ? AND ignored_player_id = ?", playerId, ignoredPlayerId); +} diff --git a/migrations/dlu/13_ignore_list.sql b/migrations/dlu/13_ignore_list.sql new file mode 100644 index 000000000..2550beb58 --- /dev/null +++ b/migrations/dlu/13_ignore_list.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS ignore_list ( + player_id BIGINT NOT NULL REFERENCES charinfo(id) ON DELETE CASCADE, + ignored_player_id BIGINT NOT NULL REFERENCES charinfo(id) ON DELETE CASCADE, + + PRIMARY KEY (player_id, ignored_player_id) +);