diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index d01d65fd..de0b395f 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -73,7 +73,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { data.Serialize(bitStream); } - SystemAddress sysAddr = player.sysAddr; + SystemAddress sysAddr = player.worldServerSysAddr; SEND_PACKET; } @@ -122,7 +122,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { requesteeFriendData.isOnline = false; requesteeFriendData.zoneID = requestor.zoneID; requestee.friends.push_back(requesteeFriendData); - requestee.sysAddr = UNASSIGNED_SYSTEM_ADDRESS; + requestee.worldServerSysAddr = UNASSIGNED_SYSTEM_ADDRESS; break; } } @@ -189,8 +189,8 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus); // Sent the best friend update here if the value is 3 if (bestFriendStatus == 3U) { - if (requestee.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true); - if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true); + if (requestee.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true); + if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true); for (auto& friendData : requestor.friends) { if (friendData.friendID == requestee.playerID) { @@ -211,7 +211,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { } } } else { - if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true); + if (requestor.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true); } } else { auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends(); @@ -384,7 +384,7 @@ void ChatPacketHandler::HandleWho(Packet* packet) { bitStream.Write(player.zoneID.GetCloneID()); bitStream.Write(request.playerName); - SystemAddress sysAddr = sender.sysAddr; + SystemAddress sysAddr = sender.worldServerSysAddr; SEND_PACKET; } @@ -418,7 +418,7 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) { } } } - SystemAddress sysAddr = sender.sysAddr; + SystemAddress sysAddr = sender.worldServerSysAddr; SEND_PACKET; } @@ -519,6 +519,28 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::GENERAL, eChatMessageResponseCode::NOTFRIENDS); } +void ChatPacketHandler::OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr) { + ChatPackets::AchievementNotify notify{}; + notify.Deserialize(bitstream); + const auto& playerData = Game::playerContainer.GetPlayerData(notify.earnerName.GetAsString()); + if (!playerData) return; + + for (const auto& myFriend : playerData.friends) { + auto& friendData = Game::playerContainer.GetPlayerData(myFriend.friendID); + if (friendData) { + notify.targetPlayerName.string = GeneralUtils::ASCIIToUTF16(friendData.playerName); + LOG_DEBUG("Sending achievement notify to %s", notify.targetPlayerName.GetAsString().c_str()); + + RakNet::BitStream worldStream; + BitStreamUtils::WriteHeader(worldStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET); + worldStream.Write(friendData.playerID); + notify.WriteHeader(worldStream); + notify.Serialize(worldStream); + Game::server->Send(worldStream, friendData.worldServerSysAddr, false); + } + } +} + void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET); @@ -537,7 +559,7 @@ void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const P bitStream.Write(responseCode); bitStream.Write(message); - SystemAddress sysAddr = routeTo.sysAddr; + SystemAddress sysAddr = routeTo.worldServerSysAddr; SEND_PACKET; } @@ -772,7 +794,7 @@ void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerD bitStream.Write(LUWString(sender.playerName.c_str())); bitStream.Write(sender.playerID); - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -799,7 +821,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool b bitStream.Write(character); } - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -824,7 +846,7 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L bitStream.Write(character); } - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -841,7 +863,7 @@ void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i bitStream.Write(i64PlayerID); - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -870,7 +892,7 @@ void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFr } bitStream.Write(zoneID); - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -896,7 +918,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD bitStream.Write(character); } - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -917,7 +939,7 @@ void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOO } bitStream.Write(zoneID); - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -959,7 +981,7 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla bitStream.Write(isBestFriend); //isBFF bitStream.Write(0); //isFTP - SystemAddress sysAddr = friendData.sysAddr; + SystemAddress sysAddr = friendData.worldServerSysAddr; SEND_PACKET; } @@ -981,7 +1003,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play bitStream.Write(LUWString(sender.playerName)); bitStream.Write(0); // This is a BFF flag however this is unused in live and does not have an implementation client side. - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -994,7 +1016,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE); bitStream.Write(responseCode); // For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver. - bitStream.Write(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS); + bitStream.Write(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.worldServerSysAddr != UNASSIGNED_SYSTEM_ADDRESS); // Then write the player name bitStream.Write(LUWString(sender.playerName)); // Then if this is an acceptance code, write the following extra info. @@ -1004,7 +1026,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla bitStream.Write(isBestFriendRequest); //isBFF bitStream.Write(0); //isFTP } - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } @@ -1018,6 +1040,6 @@ void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string bitStream.Write(isSuccessful); //isOnline bitStream.Write(LUWString(personToRemove)); - SystemAddress sysAddr = receiver.sysAddr; + SystemAddress sysAddr = receiver.worldServerSysAddr; SEND_PACKET; } diff --git a/dChatServer/ChatPacketHandler.h b/dChatServer/ChatPacketHandler.h index def9c9b9..519dfeb6 100644 --- a/dChatServer/ChatPacketHandler.h +++ b/dChatServer/ChatPacketHandler.h @@ -64,6 +64,7 @@ namespace ChatPacketHandler { void HandleTeamPromote(Packet* packet); void HandleTeamLootOption(Packet* packet); void HandleTeamStatusRequest(Packet* packet); + void OnAchievementNotify(RakNet::BitStream& bitstream, const SystemAddress& sysAddr); void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender); void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName); diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 2aceb5a7..d6728a9a 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -224,6 +224,10 @@ void HandlePacket(Packet* packet) { if (connection != eConnectionType::CHAT) return; inStream.Read(chatMessageID); + // Our packing byte wasnt there? Probably a false packet + if (inStream.GetNumberOfUnreadBits() < 8) return; + inStream.IgnoreBytes(1); + switch (chatMessageID) { case MessageType::Chat::GM_MUTE: Game::playerContainer.MuteUpdate(packet); @@ -322,6 +326,9 @@ void HandlePacket(Packet* packet) { case MessageType::Chat::SHOW_ALL: ChatPacketHandler::HandleShowAll(packet); break; + case MessageType::Chat::ACHIEVEMENT_NOTIFY: + ChatPacketHandler::OnAchievementNotify(inStream, packet->systemAddress); + break; case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE: case MessageType::Chat::WORLD_DISCONNECT_REQUEST: case MessageType::Chat::WORLD_PROXIMITY_RESPONSE: @@ -357,7 +364,6 @@ void HandlePacket(Packet* packet) { case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT: case MessageType::Chat::UGCC_REQUEST: case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE: - case MessageType::Chat::ACHIEVEMENT_NOTIFY: case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW: case MessageType::Chat::PLAYER_READY: case MessageType::Chat::GET_DONATION_TOTAL: diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index 5aa3bc14..5392fe54 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -52,7 +52,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) { if (!inStream.Read(data.zoneID)) return; if (!inStream.Read(data.muteExpire)) return; if (!inStream.Read(data.gmLevel)) return; - data.sysAddr = packet->systemAddress; + data.worldServerSysAddr = packet->systemAddress; m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName); m_PlayerCount++; @@ -241,7 +241,7 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { LOG("Tried to add player to team that already had 4 players"); const auto& player = GetPlayerData(playerID); if (!player) return; - ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!"); + ChatPackets::SendSystemMessage(player.worldServerSysAddr, u"The teams is full! You have not been added to a team!"); return; } diff --git a/dChatServer/PlayerContainer.h b/dChatServer/PlayerContainer.h index 167a73b1..4e9f3933 100644 --- a/dChatServer/PlayerContainer.h +++ b/dChatServer/PlayerContainer.h @@ -42,7 +42,7 @@ struct PlayerData { return muteExpire == 1 || muteExpire > time(NULL); } - SystemAddress sysAddr{}; + SystemAddress worldServerSysAddr{}; LWOZONEID zoneID{}; LWOOBJID playerID = LWOOBJID_EMPTY; time_t muteExpire = 0; diff --git a/dCommon/dEnums/dCommonVars.h b/dCommon/dEnums/dCommonVars.h index 70eb1dc9..085fed2e 100644 --- a/dCommon/dEnums/dCommonVars.h +++ b/dCommon/dEnums/dCommonVars.h @@ -98,6 +98,7 @@ public: constexpr LWOZONEID() noexcept = default; constexpr LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) noexcept { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; } constexpr LWOZONEID(const LWOZONEID& replacement) noexcept { *this = replacement; } + constexpr bool operator==(const LWOZONEID&) const = default; private: LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc... diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index f1f97eab..559193b7 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -27,6 +27,8 @@ #include "Character.h" #include "CDMissionEmailTable.h" +#include "ChatPackets.h" +#include "PlayerManager.h" Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) { m_MissionComponent = missionComponent; @@ -355,12 +357,25 @@ void Mission::Complete(const bool yieldRewards) { for (const auto& email : missionEmails) { const auto missionEmailBase = "MissionEmail_" + std::to_string(email.ID) + "_"; - if (email.messageType == 1) { + if (email.messageType == 1 /* Send an email to the player */) { const auto subject = "%[" + missionEmailBase + "subjectText]"; const auto body = "%[" + missionEmailBase + "bodyText]"; const auto sender = "%[" + missionEmailBase + "senderName]"; Mail::SendMail(LWOOBJID_EMPTY, sender, GetAssociate(), subject, body, email.attachmentLOT, 1); + } else if (email.messageType == 2 /* Send an announcement in chat */) { + auto* character = entity->GetCharacter(); + + ChatPackets::AchievementNotify notify{}; + notify.missionEmailID = email.ID; + notify.earningPlayerID = entity->GetObjectID(); + notify.earnerName.string = character ? GeneralUtils::ASCIIToUTF16(character->GetName()) : u""; + + // Manual write since it's sent to chat server and not a game client + RakNet::BitStream bitstream; + notify.WriteHeader(bitstream); + notify.Serialize(bitstream); + Game::chatServer->Send(&bitstream, HIGH_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); } } } diff --git a/dNet/ChatPackets.cpp b/dNet/ChatPackets.cpp index 8f4015e9..19375426 100644 --- a/dNet/ChatPackets.cpp +++ b/dNet/ChatPackets.cpp @@ -107,3 +107,28 @@ void ChatPackets::Announcement::Send() { bitStream.Write(message); SEND_PACKET_BROADCAST; } + +void ChatPackets::AchievementNotify::Serialize(RakNet::BitStream& bitstream) const { + bitstream.Write(0); // Packing + bitstream.Write(0); // Packing + bitstream.Write(0); // Packing + bitstream.Write(targetPlayerName); + bitstream.Write(0); // Packing / No way to know meaning because of not enough data. + bitstream.Write(0); // Packing / No way to know meaning because of not enough data. + bitstream.Write(0); // Packing / No way to know meaning because of not enough data. + bitstream.Write(0); // Packing / No way to know meaning because of not enough data. + bitstream.Write(missionEmailID); + bitstream.Write(earningPlayerID); + bitstream.Write(earnerName); +} + +bool ChatPackets::AchievementNotify::Deserialize(RakNet::BitStream& bitstream) { + bitstream.IgnoreBytes(13); + VALIDATE_READ(bitstream.Read(targetPlayerName)); + bitstream.IgnoreBytes(15); + VALIDATE_READ(bitstream.Read(missionEmailID)); + VALIDATE_READ(bitstream.Read(earningPlayerID)); + VALIDATE_READ(bitstream.Read(earnerName)); + + return true; +} diff --git a/dNet/ChatPackets.h b/dNet/ChatPackets.h index 0f70c8e2..b435ecab 100644 --- a/dNet/ChatPackets.h +++ b/dNet/ChatPackets.h @@ -10,6 +10,8 @@ struct SystemAddress; #include #include "dCommonVars.h" +#include "MessageType/Chat.h" +#include "BitStreamUtils.h" struct ShowAllRequest{ LWOOBJID requestor = LWOOBJID_EMPTY; @@ -34,6 +36,16 @@ namespace ChatPackets { void Send(); }; + struct AchievementNotify : public LUBitStream { + LUWString targetPlayerName{}; + uint32_t missionEmailID{}; + LWOOBJID earningPlayerID{}; + LUWString earnerName{}; + AchievementNotify() : LUBitStream(eConnectionType::CHAT, MessageType::Chat::ACHIEVEMENT_NOTIFY) {} + void Serialize(RakNet::BitStream& bitstream) const override; + bool Deserialize(RakNet::BitStream& bitstream) override; + }; + void SendChatMessage(const SystemAddress& sysAddr, char chatChannel, const std::string& senderName, LWOOBJID playerObjectID, bool senderMythran, const std::u16string& message); void SendSystemMessage(const SystemAddress& sysAddr, const std::u16string& message, bool broadcast = false); void SendMessageFail(const SystemAddress& sysAddr);