From 06217ad5e30981a3e8b94093dca2c0bbc4d28b80 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Sun, 10 Jul 2022 17:59:07 -0700 Subject: [PATCH] Address friends list IDs and removal of friends (#628) * Add friends list migration * Change friends to use charID Update friends table to use charID and not LWOOBJID variant. * Fix remove friend Fix remove friend and make the query more readable at a glance. * Add and remove friends in the container Properly add and remove friends in the player container --- dChatServer/ChatPacketHandler.cpp | 145 +++++++++++--------- migrations/dlu/4_friends_list_objectids.sql | 1 + 2 files changed, 84 insertions(+), 62 deletions(-) create mode 100644 migrations/dlu/4_friends_list_objectids.sql diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index 899fd355..82f24ffa 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -21,10 +21,17 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { auto player = playerContainer.GetPlayerData(playerID); if (!player) return; - //Get our friends list from the Db: - auto stmt = Database::CreatePreppedStmt("SELECT * FROM friends WHERE player_id = ? OR friend_id = ?"); - stmt->setUInt64(1, playerID); - stmt->setUInt64(2, playerID); + //Get our friends list from the Db. Using a derived table since the friend of a player can be in either column. + auto stmt = Database::CreatePreppedStmt( + "SELECT fr.requested_player, best_friend, ci.name FROM " + "(SELECT CASE " + "WHEN player_id = ? THEN friend_id " + "WHEN friend_id = ? THEN player_id " + "END AS requested_player, best_friend FROM friends) AS fr " + "JOIN charinfo AS ci ON ci.id = fr.requested_player " + "WHERE fr.requested_player IS NOT NULL;"); + stmt->setUInt(1, static_cast(playerID)); + stmt->setUInt(2, static_cast(playerID)); std::vector friends; @@ -32,27 +39,16 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { while (res->next()) { FriendData fd; fd.isFTP = false; // not a thing in DLU - fd.friendID = res->getInt64(1); - if (fd.friendID == playerID) fd.friendID = res->getUInt64(2); + fd.friendID = res->getUInt(1); + GeneralUtils::SetBit(fd.friendID, static_cast(eObjectBits::OBJECT_BIT_PERSISTENT)); + GeneralUtils::SetBit(fd.friendID, static_cast(eObjectBits::OBJECT_BIT_CHARACTER)); - fd.isBestFriend = res->getInt(3) == 2; //0 = friends, 1 = requested, 2 = bffs - - //We need to find their name as well: - { - auto stmt = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE id=? limit 1"); - stmt->setInt(1, fd.friendID); - - auto res = stmt->executeQuery(); - while (res->next()) { - fd.friendName = res->getString(1); - } - - delete res; - delete stmt; - } + fd.isBestFriend = res->getInt(2) == 2; //0 = friends, 1 = requested, 2 = bffs + fd.friendName = res->getString(3); //Now check if they're online: auto fr = playerContainer.GetPlayerData(fd.friendID); + Game::logger->Log("ChatPacketHandler", "friend is %llu\n", fd.friendID); if (fr) { fd.isOnline = true; fd.zoneID = fr->zoneID; @@ -69,7 +65,9 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { } delete res; + res = nullptr; delete stmt; + stmt = nullptr; //Now, we need to send the friendlist to the server they came from: CBITSTREAM; @@ -100,8 +98,6 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { std::string playerName = PacketUtils::ReadString(0x14, packet, true); //There's another bool here to determine if it's a best friend request, but we're not handling it right now. - //PacketUtils::SavePacket("FriendRequest.bin", (char*)inStream.GetData(), inStream.GetNumberOfBytesUsed()); - //We need to check to see if the player is actually online or not: auto targetData = playerContainer.GetPlayerData(playerName); if (targetData) { @@ -118,23 +114,46 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { uint8_t responseCode = packet->data[0x14]; std::string friendName = PacketUtils::ReadString(0x15, packet, true); - Game::logger->Log("ChatPacketHandler", "Friend response code: %i\n", responseCode); - - if (responseCode != 0) return; //If we're not accepting the request, end here, do not insert to friends table. - - PacketUtils::SavePacket("HandleFriendResponse.bin", (char*)inStream.GetData(), inStream.GetNumberOfBytesUsed()); - //Now to try and find both of these: auto goonA = playerContainer.GetPlayerData(playerID); auto goonB = playerContainer.GetPlayerData(friendName); if (!goonA || !goonB) return; + if (responseCode != 0) return; //If we're not accepting the request, end here, do not insert to friends table. + + for (auto friendData : goonA->friends) { + if (friendData.friendID == goonB->playerID) return; + } + + for (auto friendData : goonB->friends) { + if (friendData.friendID == goonA->playerID) return; + } + + // Add the goons to their friends list + FriendData goonAData; + goonAData.zoneID = goonA->zoneID; + goonAData.friendID = goonA->playerID; + goonAData.friendName = goonA->playerName; + goonAData.isBestFriend = false; + goonAData.isFTP = false; + goonAData.isOnline = true; + goonB->friends.push_back(goonAData); + + FriendData goonBData; + goonBData.zoneID = goonB->zoneID; + goonBData.friendID = goonB->playerID; + goonBData.friendName = goonB->playerName; + goonBData.isBestFriend = false; + goonBData.isFTP = false; + goonBData.isOnline = true; + goonA->friends.push_back(goonBData); + SendFriendResponse(goonB, goonA, responseCode); SendFriendResponse(goonA, goonB, responseCode); //Do we need to send it to both? I think so so both get the updated friendlist but... idk. - auto stmt = Database::CreatePreppedStmt("INSERT INTO `friends`(`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?)"); - stmt->setUInt64(1, goonA->playerID); - stmt->setUInt64(2, goonB->playerID); + auto stmt = Database::CreatePreppedStmt("INSERT IGNORE INTO `friends` (`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?)"); + stmt->setUInt(1, static_cast(goonA->playerID)); + stmt->setUInt(2, static_cast(goonB->playerID)); stmt->setInt(3, 0); stmt->execute(); delete stmt; @@ -145,50 +164,61 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { LWOOBJID playerID; inStream.Read(playerID); inStream.Read(playerID); - std::string friendName = PacketUtils::ReadString(16, packet, true); + std::string friendName = PacketUtils::ReadString(0x14, packet, true); //we'll have to query the db here to find the user, since you can delete them while they're offline. //First, we need to find their ID: - auto stmt = Database::CreatePreppedStmt("select id from charinfo where name=? limit 1;"); + auto stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? LIMIT 1;"); stmt->setString(1, friendName.c_str()); LWOOBJID friendID = 0; auto res = stmt->executeQuery(); while (res->next()) { - friendID = res->getUInt64(1); + friendID = res->getUInt(1); } + // Convert friendID to LWOOBJID + GeneralUtils::SetBit(friendID, static_cast(eObjectBits::OBJECT_BIT_PERSISTENT)); + GeneralUtils::SetBit(friendID, static_cast(eObjectBits::OBJECT_BIT_CHARACTER)); + delete res; + res = nullptr; delete stmt; + stmt = nullptr; - //Set our bits to convert to the BIG BOY objectID. - friendID = GeneralUtils::ClearBit(friendID, OBJECT_BIT_CHARACTER); - friendID = GeneralUtils::ClearBit(friendID, OBJECT_BIT_PERSISTENT); - - //YEET: - auto deletestmt = Database::CreatePreppedStmt("DELETE FROM `friends` WHERE player_id=? AND friend_id=? LIMIT 1"); - deletestmt->setUInt64(1, playerID); - deletestmt->setUInt64(2, friendID); + auto deletestmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"); + deletestmt->setUInt(1, static_cast(playerID)); + deletestmt->setUInt(2, static_cast(friendID)); + deletestmt->setUInt(3, static_cast(friendID)); + deletestmt->setUInt(4, static_cast(playerID)); deletestmt->execute(); - delete deletestmt; - //because I'm lazy and they can be reversed: - { - auto deletestmt = Database::CreatePreppedStmt("DELETE FROM `friends` WHERE player_id=? AND friend_id=? LIMIT 1"); - deletestmt->setUInt64(1, friendID); - deletestmt->setUInt64(2, playerID); - deletestmt->execute(); - delete deletestmt; - } + delete deletestmt; + deletestmt = nullptr; //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); if (goonA) { + // Remove the friend from our list of friends + for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) { + if ((*friendData).friendID == friendID) { + goonA->friends.erase(friendData); + break; + } + } SendRemoveFriend(goonA, friendName, true); } - + auto goonB = playerContainer.GetPlayerData(friendID); if (!goonB) return; + // Do it again for other person + for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) { + if ((*friendData).friendID == playerID) { + goonB->friends.erase(friendData); + break; + } + } + std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID)); SendRemoveFriend(goonB, goonAName, true); } @@ -217,8 +247,6 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) Game::logger->Log("ChatPacketHandler", "Got a message from (%s) [%d]: %s\n", senderName.c_str(), channel, message.c_str()); - //PacketUtils::SavePacket("chat.bin", reinterpret_cast(packet->data), packet->length); - if (channel != 8) return; auto* team = playerContainer.GetTeam(playerID); @@ -454,8 +482,6 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet) playerContainer.RemoveMember(team, kickedId, false, true, false); } - - //PacketUtils::SavePacket("kick.bin", reinterpret_cast(packet->data), packet->length); } void ChatPacketHandler::HandleTeamPromote(Packet* packet) @@ -481,8 +507,6 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet) playerContainer.PromoteMember(team, promoted->playerID); } - - //PacketUtils::SavePacket("promote.bin", reinterpret_cast(packet->data), packet->length); } void ChatPacketHandler::HandleTeamLootOption(Packet* packet) @@ -509,8 +533,6 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet) playerContainer.UpdateTeamsOnWorld(team, false); } - - //PacketUtils::SavePacket("option.bin", reinterpret_cast(packet->data), packet->length); } void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) @@ -560,7 +582,6 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) const auto memberName = playerContainer.GetName(memberId); - //ChatPacketHandler::SendTeamAddPlayer(otherMember, false, false, false, data->playerID, leaderName, data->zoneID); if (otherMember != nullptr) { ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID); diff --git a/migrations/dlu/4_friends_list_objectids.sql b/migrations/dlu/4_friends_list_objectids.sql new file mode 100644 index 00000000..efb3a7ae --- /dev/null +++ b/migrations/dlu/4_friends_list_objectids.sql @@ -0,0 +1 @@ +UPDATE friends SET player_id = player_id % 0x100000000, friend_id = friend_id % 0x100000000; \ No newline at end of file