From df3515f4742b9271899fad2329e5624e6acb7b40 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 Nov 2023 03:55:12 -0800 Subject: [PATCH 01/10] IgnoreList: Add and Populate fully working --- dChatServer/CMakeLists.txt | 1 + dChatServer/ChatIgnoreList.cpp | 121 +++++++++++++++++++++++++++++++++ dChatServer/ChatIgnoreList.h | 11 +++ dChatServer/ChatServer.cpp | 7 +- dChatServer/PlayerContainer.h | 14 ++++ 5 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 dChatServer/ChatIgnoreList.cpp create mode 100644 dChatServer/ChatIgnoreList.h diff --git a/dChatServer/CMakeLists.txt b/dChatServer/CMakeLists.txt index 9a47803d..34d58a3a 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 00000000..a84c3cda --- /dev/null +++ b/dChatServer/ChatIgnoreList.cpp @@ -0,0 +1,121 @@ +#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" + +extern PlayerContainer playerContainer; + +enum IgnoreReponse : uint8_t { + AddIgnoreResponse = 32, + GetIgnoreListResponse = 34, +}; + +void ChatIgnoreList::GetIgnoreList(Packet* packet) { + LOG_DEBUG("Getting ignore list"); + + CINSTREAM_SKIP_HEADER; + LWOOBJID playerId; + inStream.Read(playerId); + + auto* receiver = playerContainer.GetPlayerData(playerId); + if (!receiver) { + LOG("Tried to get ignore list, but player %llu not found in container", playerId); + return; + } + + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver->playerID); + + //portion that will get routed: + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, IgnoreReponse::GetIgnoreListResponse); + 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); +} + +// Remove from ignore list and response +// Not much else to do with editing the list, maybe more messages are needed for refreshes or something +// but you can only add or remove from the list, and you only request the list on world start so pretty small file +// After the above all work, move to implement the actual ignore functionality in the chat server +enum class IgnoreResponse : uint8_t { + SUCCESS, + ALREADY_IGNORED, + PLAYER_NOT_FOUND, + GENERAL_ERROR, +}; + +void ChatIgnoreList::AddIgnore(Packet* packet) { + LOG_DEBUG("Adding ignore"); + + CINSTREAM_SKIP_HEADER; + LWOOBJID playerId; + inStream.Read(playerId); + + auto* receiver = 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 toIgnoreName(33); + inStream.Read(toIgnoreName); + std::string toIgnoreStr = toIgnoreName.GetAsString(); + + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver->playerID); + + //portion that will get routed: + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, IgnoreReponse::AddIgnoreResponse); + + // 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(IgnoreResponse::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(IgnoreResponse::ALREADY_IGNORED); + } else { + auto* playerData = 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()); + + bitStream.Write(IgnoreResponse::PLAYER_NOT_FOUND); + } else { + ignoredPlayerId = player->id; + 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(IgnoreResponse::SUCCESS); + } + } + } + + LUWString playerNameSend(toIgnoreStr, 33); + bitStream.Write(playerNameSend); + bitStream.Write(ignoredPlayerId); + + Game::server->Send(&bitStream, packet->systemAddress, false); +} diff --git a/dChatServer/ChatIgnoreList.h b/dChatServer/ChatIgnoreList.h new file mode 100644 index 00000000..48170369 --- /dev/null +++ b/dChatServer/ChatIgnoreList.h @@ -0,0 +1,11 @@ +#ifndef __CHATIGNORELIST__H__ +#define __CHATIGNORELIST__H__ + +struct Packet; + +namespace ChatIgnoreList { + void GetIgnoreList(Packet* packet); + void AddIgnore(Packet* packet); +}; + +#endif //!__CHATIGNORELIST__H__ diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index b41ad4ec..6671b047 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" @@ -234,7 +235,11 @@ 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::TEAM_GET_STATUS: diff --git a/dChatServer/PlayerContainer.h b/dChatServer/PlayerContainer.h index d055ed95..51bff6c1 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; }; From 511407c8eaff5246437b2661bd4eab65460bd149 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 Nov 2023 17:33:52 -0800 Subject: [PATCH 02/10] Removing ignore works now --- dChatServer/ChatIgnoreList.cpp | 52 +++++++++++++++-- dChatServer/ChatIgnoreList.h | 1 + dChatServer/ChatPacketHandler.cpp | 94 +++++++++++++++---------------- dChatServer/ChatServer.cpp | 18 +++--- dCommon/Game.h | 2 + 5 files changed, 106 insertions(+), 61 deletions(-) diff --git a/dChatServer/ChatIgnoreList.cpp b/dChatServer/ChatIgnoreList.cpp index a84c3cda..bcb771e7 100644 --- a/dChatServer/ChatIgnoreList.cpp +++ b/dChatServer/ChatIgnoreList.cpp @@ -9,10 +9,9 @@ #include "Database.h" -extern PlayerContainer playerContainer; - enum IgnoreReponse : uint8_t { AddIgnoreResponse = 32, + RemoveIgnoreResponse = 33, GetIgnoreListResponse = 34, }; @@ -23,7 +22,7 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) { LWOOBJID playerId; inStream.Read(playerId); - auto* receiver = playerContainer.GetPlayerData(playerId); + auto* receiver = Game::playerContainer.GetPlayerData(playerId); if (!receiver) { LOG("Tried to get ignore list, but player %llu not found in container", playerId); return; @@ -63,11 +62,12 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { LWOOBJID playerId; inStream.Read(playerId); - auto* receiver = playerContainer.GetPlayerData(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 toIgnoreName(33); @@ -92,7 +92,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { bitStream.Write(IgnoreResponse::ALREADY_IGNORED); } else { - auto* playerData = playerContainer.GetPlayerData(toIgnoreStr); + auto* playerData = Game::playerContainer.GetPlayerData(toIgnoreStr); if (!playerData) { // Fall back to query auto player = Database::Get()->GetCharacterInfo(toIgnoreStr); @@ -119,3 +119,45 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { Game::server->Send(&bitStream, packet->systemAddress, false); } + +void ChatIgnoreList::RemoveIgnore(Packet* packet) { + LOG_DEBUG("Removing ignore"); + + 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(); + + LOG("Removing ignore for %s", removedIgnoreStr.c_str()); + 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; + } + + receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end()); + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + + bitStream.Write(receiver->playerID); + + //portion that will get routed: + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, IgnoreReponse::RemoveIgnoreResponse); + + 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 index 48170369..4e90677b 100644 --- a/dChatServer/ChatIgnoreList.h +++ b/dChatServer/ChatIgnoreList.h @@ -6,6 +6,7 @@ struct Packet; namespace ChatIgnoreList { void GetIgnoreList(Packet* packet); void AddIgnore(Packet* packet); + void RemoveIgnore(Packet* packet); }; #endif //!__CHATIGNORELIST__H__ diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index 0a3caa8e..46a54888 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 6671b047..fccbdb23 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -35,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; @@ -109,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(); @@ -201,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: { @@ -242,6 +240,10 @@ void HandlePacket(Packet* packet) { ChatIgnoreList::AddIgnore(packet); break; + case eChatMessageType::REMOVE_IGNORE: + ChatIgnoreList::RemoveIgnore(packet); + break; + case eChatMessageType::TEAM_GET_STATUS: ChatPacketHandler::HandleTeamStatusRequest(packet); break; diff --git a/dCommon/Game.h b/dCommon/Game.h index 79a271ec..305b4f7a 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; } From d193fe61be5e6915c9440d71c3512f20665dd464 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 Nov 2023 18:05:26 -0800 Subject: [PATCH 03/10] Database working for ignores --- dChatServer/ChatIgnoreList.cpp | 14 ++++++++++++ dDatabase/GameDatabase/GameDatabase.h | 3 ++- dDatabase/GameDatabase/ITables/IIgnoreList.h | 20 +++++++++++++++++ dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 3 +++ .../GameDatabase/MySQL/Tables/CMakeLists.txt | 1 + .../GameDatabase/MySQL/Tables/IgnoreList.cpp | 22 +++++++++++++++++++ migrations/dlu/13_ignore_list.sql | 6 +++++ 7 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 dDatabase/GameDatabase/ITables/IIgnoreList.h create mode 100644 dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp create mode 100644 migrations/dlu/13_ignore_list.sql diff --git a/dChatServer/ChatIgnoreList.cpp b/dChatServer/ChatIgnoreList.cpp index bcb771e7..1eebb570 100644 --- a/dChatServer/ChatIgnoreList.cpp +++ b/dChatServer/ChatIgnoreList.cpp @@ -28,6 +28,18 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) { 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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); bitStream.Write(receiver->playerID); @@ -102,6 +114,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { bitStream.Write(IgnoreResponse::PLAYER_NOT_FOUND); } else { ignoredPlayerId = player->id; + Database::Get()->AddIgnore(static_cast(playerId), static_cast(ignoredPlayerId)); GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER); GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT); @@ -146,6 +159,7 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) { return; } + Database::Get()->RemoveIgnore(static_cast(playerId), static_cast(toRemove->playerId)); receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end()); CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); diff --git a/dDatabase/GameDatabase/GameDatabase.h b/dDatabase/GameDatabase/GameDatabase.h index 7d8c7de9..a79bc8d4 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" namespace sql { class Statement; @@ -38,7 +39,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 IAccounts, public IActivityLog, 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 00000000..cf831203 --- /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 bed79bb7..26bfd2d9 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -103,6 +103,9 @@ public: 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; private: // Generic query functions that can be used for any query. diff --git a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt index e9593ba9..51b5d34a 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt +++ b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt @@ -6,6 +6,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 00000000..ec90e341 --- /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 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 00000000..2550beb5 --- /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) +); From 12ea2dfb2e6afccad1e011cf95e75f247fc34b80 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 Nov 2023 18:37:06 -0800 Subject: [PATCH 04/10] Works for online players now --- dChatServer/ChatIgnoreList.cpp | 31 +++++++++++++------ .../GameDatabase/MySQL/Tables/IgnoreList.cpp | 2 +- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/dChatServer/ChatIgnoreList.cpp b/dChatServer/ChatIgnoreList.cpp index 1eebb570..d97c4536 100644 --- a/dChatServer/ChatIgnoreList.cpp +++ b/dChatServer/ChatIgnoreList.cpp @@ -28,6 +28,11 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) { 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); @@ -104,25 +109,31 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { bitStream.Write(IgnoreResponse::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()); - - bitStream.Write(IgnoreResponse::PLAYER_NOT_FOUND); } else { ignoredPlayerId = player->id; - 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(IgnoreResponse::SUCCESS); } + } 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(IgnoreResponse::SUCCESS); + } else { + bitStream.Write(IgnoreResponse::PLAYER_NOT_FOUND); } } diff --git a/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp b/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp index ec90e341..283df324 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp @@ -14,7 +14,7 @@ std::vector MySQLDatabase::GetIgnoreList(const uint32_t playe } void MySQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) { - ExecuteInsert("INSERT INTO ignore_list (player_id, ignored_player_id) VALUES (?, ?)", playerId, 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) { From 81b1b73f040fc43321d57cc712f6e9ff25785d05 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 Nov 2023 19:15:57 -0800 Subject: [PATCH 05/10] fix gating --- .../CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp index 1a9338a0..9b133155 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp @@ -44,7 +44,7 @@ std::vector CDFeatureGatingTable::Query(std::function= feature) { + if (entry.featureName == feature.featureName && feature >= entry) { return true; } } From 59bf91b14f3833cf7e4030441d02d20974f16571 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 Nov 2023 20:07:27 -0800 Subject: [PATCH 06/10] fix up enum names Update ChatIgnoreList.cpp Add extra check for size on adding Cant happen without hacks, but worth checking anyways Update ChatIgnoreList.cpp --- dChatServer/ChatIgnoreList.cpp | 43 ++++++++++++++++------------------ dChatServer/ChatIgnoreList.h | 15 ++++++++++++ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/dChatServer/ChatIgnoreList.cpp b/dChatServer/ChatIgnoreList.cpp index d97c4536..1c9000e5 100644 --- a/dChatServer/ChatIgnoreList.cpp +++ b/dChatServer/ChatIgnoreList.cpp @@ -9,11 +9,9 @@ #include "Database.h" -enum IgnoreReponse : uint8_t { - AddIgnoreResponse = 32, - RemoveIgnoreResponse = 33, - GetIgnoreListResponse = 34, -}; +// 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 ChatIgnoreList::GetIgnoreList(Packet* packet) { LOG_DEBUG("Getting ignore list"); @@ -50,28 +48,20 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) { bitStream.Write(receiver->playerID); //portion that will get routed: - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, IgnoreReponse::GetIgnoreListResponse); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, 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); } -// Remove from ignore list and response -// Not much else to do with editing the list, maybe more messages are needed for refreshes or something -// but you can only add or remove from the list, and you only request the list on world start so pretty small file -// After the above all work, move to implement the actual ignore functionality in the chat server -enum class IgnoreResponse : uint8_t { - SUCCESS, - ALREADY_IGNORED, - PLAYER_NOT_FOUND, - GENERAL_ERROR, -}; - void ChatIgnoreList::AddIgnore(Packet* packet) { LOG_DEBUG("Adding ignore"); @@ -85,6 +75,12 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { 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); @@ -96,18 +92,18 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { bitStream.Write(receiver->playerID); //portion that will get routed: - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, IgnoreReponse::AddIgnoreResponse); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, 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(IgnoreResponse::GENERAL_ERROR); + 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(IgnoreResponse::ALREADY_IGNORED); + bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED); } else { // Get the playerId falling back to query if not online auto* playerData = Game::playerContainer.GetPlayerData(toIgnoreStr); @@ -131,9 +127,9 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayerId, toIgnoreStr }); LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str()); - bitStream.Write(IgnoreResponse::SUCCESS); + bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS); } else { - bitStream.Write(IgnoreResponse::PLAYER_NOT_FOUND); + bitStream.Write(ChatIgnoreList::AddResponse::PLAYER_NOT_FOUND); } } @@ -172,13 +168,14 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) { Database::Get()->RemoveIgnore(static_cast(playerId), static_cast(toRemove->playerId)); receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end()); + CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); bitStream.Write(receiver->playerID); //portion that will get routed: - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, IgnoreReponse::RemoveIgnoreResponse); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, ChatIgnoreList::Response::REMOVE_IGNORE); bitStream.Write(0); LUWString playerNameSend(removedIgnoreStr, 33); diff --git a/dChatServer/ChatIgnoreList.h b/dChatServer/ChatIgnoreList.h index 4e90677b..c713c966 100644 --- a/dChatServer/ChatIgnoreList.h +++ b/dChatServer/ChatIgnoreList.h @@ -3,10 +3,25 @@ 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__ From 1001e4152891ab14774d01b8798a0c3887d79f05 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 Nov 2023 20:33:30 -0800 Subject: [PATCH 07/10] simplify code path --- dChatServer/ChatIgnoreList.cpp | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/dChatServer/ChatIgnoreList.cpp b/dChatServer/ChatIgnoreList.cpp index 1c9000e5..94d1ac7a 100644 --- a/dChatServer/ChatIgnoreList.cpp +++ b/dChatServer/ChatIgnoreList.cpp @@ -13,9 +13,15 @@ // 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 ChatIgnoreList::GetIgnoreList(Packet* packet) { - LOG_DEBUG("Getting ignore list"); +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); @@ -44,11 +50,7 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) { } CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); - - //portion that will get routed: - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, ChatIgnoreList::Response::GET_IGNORE); + 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 @@ -63,8 +65,6 @@ void ChatIgnoreList::GetIgnoreList(Packet* packet) { } void ChatIgnoreList::AddIgnore(Packet* packet) { - LOG_DEBUG("Adding ignore"); - CINSTREAM_SKIP_HEADER; LWOOBJID playerId; inStream.Read(playerId); @@ -88,11 +88,7 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { std::string toIgnoreStr = toIgnoreName.GetAsString(); CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); - - //portion that will get routed: - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, ChatIgnoreList::Response::ADD_IGNORE); + WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::ADD_IGNORE); // Check if the player exists LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY; @@ -141,8 +137,6 @@ void ChatIgnoreList::AddIgnore(Packet* packet) { } void ChatIgnoreList::RemoveIgnore(Packet* packet) { - LOG_DEBUG("Removing ignore"); - CINSTREAM_SKIP_HEADER; LWOOBJID playerId; inStream.Read(playerId); @@ -159,7 +153,6 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) { inStream.Read(removedIgnoreName); std::string removedIgnoreStr = removedIgnoreName.GetAsString(); - LOG("Removing ignore for %s", removedIgnoreStr.c_str()); 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()); @@ -170,12 +163,7 @@ void ChatIgnoreList::RemoveIgnore(Packet* packet) { receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end()); CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - - bitStream.Write(receiver->playerID); - - //portion that will get routed: - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, ChatIgnoreList::Response::REMOVE_IGNORE); + WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::REMOVE_IGNORE); bitStream.Write(0); LUWString playerNameSend(removedIgnoreStr, 33); From 0217f88c445b43365a55438e86f590eb296254f6 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 22 Nov 2023 08:06:09 -0600 Subject: [PATCH 08/10] pet emote fix (#1315) --- dGame/dGameMessages/GameMessages.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 4cddebff..34b031d3 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -5017,6 +5017,14 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) if (emoteID == 0) return; std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote + CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable(); + if (emotes) { + CDEmoteTable* emote = emotes->GetEmote(emoteID); + if (emote) sAnimationName = emote->animationName; + } + + RenderComponent::PlayAnimation(entity, sAnimationName); + MissionComponent* missionComponent = entity->GetComponent(); if (!missionComponent) return; @@ -5042,14 +5050,6 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); } } - - CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable(); - if (emotes) { - CDEmoteTable* emote = emotes->GetEmote(emoteID); - if (emote) sAnimationName = emote->animationName; - } - - RenderComponent::PlayAnimation(entity, sAnimationName); } void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { From fd20baaf09b141cb94710d504403ebdc69c500f4 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Thu, 23 Nov 2023 03:48:06 -0800 Subject: [PATCH 09/10] Fix UB in remote input info (#1316) Yes i should have made this first no this wouldnt have happened with rust --- dGame/dComponents/VehiclePhysicsComponent.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dGame/dComponents/VehiclePhysicsComponent.h b/dGame/dComponents/VehiclePhysicsComponent.h index 69f8579c..94ba650a 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.h +++ b/dGame/dComponents/VehiclePhysicsComponent.h @@ -6,6 +6,13 @@ #include "eReplicaComponentType.h" struct RemoteInputInfo { + RemoteInputInfo() { + m_RemoteInputX = 0; + m_RemoteInputY = 0; + m_IsPowersliding = false; + m_IsModified = false; + } + void operator=(const RemoteInputInfo& other) { m_RemoteInputX = other.m_RemoteInputX; m_RemoteInputY = other.m_RemoteInputY; From 8a15906885de35b8c74e54b695d93cd68e48e616 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Fri, 24 Nov 2023 01:46:26 -0800 Subject: [PATCH 10/10] fix: Racing end of race leaderboards now work (#1317) * Fix UB in remote input info Yes i should have made this first no this wouldnt have happened with rust * fix end of race leaderboard Tested that with two players, both players see the others time at the end of the race and all other metrics are shown correctly. Technically the outBitStream->Write(static_cast(m_RacingPlayers.size())); should only be written once but how we do it now it is written as we load players in and this is the cheap option compared to the number of bits we are supposed to waste at the end of races --- dGame/dComponents/RacingControlComponent.cpp | 108 +++++++++---------- dGame/dComponents/RacingControlComponent.h | 8 +- 2 files changed, 52 insertions(+), 64 deletions(-) diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index e5f2dbe1..a4737d9e 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -51,7 +51,7 @@ RacingControlComponent::RacingControlComponent(Entity* parent) m_MainWorld = 1200; const auto worldID = Game::server->GetZoneID(); - if (Game::zoneManager->CheckIfAccessibleZone((worldID/10)*10)) m_MainWorld = (worldID/10)*10; + if (Game::zoneManager->CheckIfAccessibleZone((worldID / 10) * 10)) m_MainWorld = (worldID / 10) * 10; m_ActivityID = 42; CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); @@ -72,7 +72,7 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) { // If the race has already started, send the player back to the main world. if (m_Loaded || !vehicle) { auto* playerInstance = dynamic_cast(player); - if(playerInstance){ + if (playerInstance) { playerInstance->SendToZone(m_MainWorld); } return; @@ -106,12 +106,12 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, if (item == nullptr) { LOG("Failed to find item"); auto* playerInstance = dynamic_cast(player); - if(playerInstance){ + if (playerInstance) { m_LoadedPlayers--; playerInstance->SendToZone(m_MainWorld); } return; - + } // Calculate the vehicle's starting position. @@ -213,6 +213,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, 0, 0, 0 }); + m_AllPlayersReady = false; } // Construct and serialize everything when done. @@ -330,7 +331,7 @@ void RacingControlComponent::OnRequestDie(Entity* player) { // Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live. if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination); Game::entityManager->SerializeEntity(vehicle); - }); + }); auto* characterComponent = player->GetComponent(); if (characterComponent != nullptr) { @@ -384,11 +385,11 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu // Calculate the score, different loot depending on player count auto playersRating = m_LoadedPlayers; - if(m_LoadedPlayers == 1 && m_SoloRacing) { + if (m_LoadedPlayers == 1 && m_SoloRacing) { playersRating *= 2; } - const auto score = playersRating * 10 + data->finished; + const auto score = playersRating * 10 + data->finished; Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score); // Giving rewards @@ -436,64 +437,82 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu void RacingControlComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { // BEGIN Scripted Activity - outBitStream->Write1(); outBitStream->Write(static_cast(m_RacingPlayers.size())); for (const auto& player : m_RacingPlayers) { outBitStream->Write(player.playerID); - for (int i = 0; i < 10; i++) { - outBitStream->Write(player.data[i]); - } + outBitStream->Write(player.data[0]); + if (player.finished != 0) outBitStream->Write(player.raceTime); + else outBitStream->Write(player.data[1]); + if (player.finished != 0) outBitStream->Write(player.bestLapTime); + else outBitStream->Write(player.data[2]); + if (player.finished == 1) outBitStream->Write(1.0f); + else outBitStream->Write(player.data[3]); + outBitStream->Write(player.data[4]); + outBitStream->Write(player.data[5]); + outBitStream->Write(player.data[6]); + outBitStream->Write(player.data[7]); + outBitStream->Write(player.data[8]); + outBitStream->Write(player.data[9]); } // END Scripted Activity - outBitStream->Write1(); // Dirty? + outBitStream->Write1(); outBitStream->Write(static_cast(m_RacingPlayers.size())); - outBitStream->Write(!m_RacingPlayers.empty()); - if (!m_RacingPlayers.empty()) { + outBitStream->Write(!m_AllPlayersReady); + if (!m_AllPlayersReady) { + int32_t numReady = 0; for (const auto& player : m_RacingPlayers) { - outBitStream->Write1(); // Has more date - + outBitStream->Write1(); // Has more player data outBitStream->Write(player.playerID); outBitStream->Write(player.vehicleID); outBitStream->Write(player.playerIndex); outBitStream->Write(player.playerLoaded); + if (player.playerLoaded) numReady++; } outBitStream->Write0(); // No more data + if (numReady == m_RacingPlayers.size()) m_AllPlayersReady = true; } outBitStream->Write(!m_RacingPlayers.empty()); if (!m_RacingPlayers.empty()) { for (const auto& player : m_RacingPlayers) { + if (player.finished == 0) continue; outBitStream->Write1(); // Has more date outBitStream->Write(player.playerID); - outBitStream->Write(0); + outBitStream->Write(player.finished); } outBitStream->Write0(); // No more data } - outBitStream->Write1(); // Dirty? - - outBitStream->Write(m_RemainingLaps); - - outBitStream->Write(static_cast(m_PathName.size())); - for (const auto character : m_PathName) { - outBitStream->Write(character); + outBitStream->Write(bIsInitialUpdate); + if (bIsInitialUpdate) { + outBitStream->Write(m_RemainingLaps); + outBitStream->Write(static_cast(m_PathName.size())); + for (const auto character : m_PathName) { + outBitStream->Write(character); + } } - outBitStream->Write1(); // ??? - outBitStream->Write1(); // ??? + outBitStream->Write(!m_RacingPlayers.empty()); + if (!m_RacingPlayers.empty()) { + for (const auto& player : m_RacingPlayers) { + if (player.finished == 0) continue; + outBitStream->Write1(); // Has more data + outBitStream->Write(player.playerID); + outBitStream->Write(player.bestLapTime); + outBitStream->Write(player.raceTime); + } - outBitStream->Write(m_LeadingPlayer); - outBitStream->Write(m_RaceBestLap); - outBitStream->Write(m_RaceBestTime); + outBitStream->Write0(); // No more data + } } RacingPlayerInfo* RacingControlComponent::GetPlayerData(LWOOBJID playerID) { @@ -569,7 +588,7 @@ void RacingControlComponent::Update(float deltaTime) { LoadPlayerVehicle(player, positionNumber + 1, true); - m_Loaded = true; + Game::entityManager->SerializeEntity(m_Parent); } m_Loaded = true; @@ -757,6 +776,8 @@ void RacingControlComponent::Update(float deltaTime) { continue; } + if (m_Finished != 0) Game::entityManager->SerializeEntity(m_Parent); + // Loop through all the waypoints and see if the player has reached a // new checkpoint uint32_t respawnIndex = 0; @@ -849,8 +870,6 @@ void RacingControlComponent::Update(float deltaTime) { if (characterComponent != nullptr) { characterComponent->TrackRaceCompleted(m_Finished == 1); } - - // TODO: Figure out how to update the GUI leaderboard. } } @@ -865,28 +884,3 @@ void RacingControlComponent::Update(float deltaTime) { } } } - -std::string RacingControlComponent::FormatTimeString(time_t time) { - int32_t min = time / 60; - time -= min * 60; - int32_t sec = time; - - std::string minText; - std::string secText; - - if (min <= 0) { - minText = "0"; - } else { - minText = std::to_string(min); - } - - if (sec <= 0) { - secText = "00"; - } else if (sec <= 9) { - secText = "0" + std::to_string(sec); - } else { - secText = std::to_string(sec); - } - - return minText + ":" + secText + ".00"; -} diff --git a/dGame/dComponents/RacingControlComponent.h b/dGame/dComponents/RacingControlComponent.h index 3dcb730e..47341aae 100644 --- a/dGame/dComponents/RacingControlComponent.h +++ b/dGame/dComponents/RacingControlComponent.h @@ -151,13 +151,6 @@ public: */ RacingPlayerInfo* GetPlayerData(LWOOBJID playerID); - /** - * Formats a time to a string, currently unused - * @param time the time to format - * @return the time formatted as string - */ - static std::string FormatTimeString(time_t time); - private: /** @@ -251,4 +244,5 @@ private: * Value for message box response to know if we are exiting the race via the activity dialogue */ const int32_t m_ActivityExitConfirm = 1; + bool m_AllPlayersReady = false; };