#include "WorldPackets.h" #include "Amf3.h" #include "dConfig.h" #include "GameMessages.h" #include "Entity.h" #include "EntityManager.h" #include "UserManager.h" #include "User.h" #include "Character.h" #include "dChatFilter.h" #include "ChatPackets.h" #include "ClientPackets.h" #include "Database.h" namespace WorldPackets { bool UIHelpTop5::Deserialize(RakNet::BitStream& bitStream) { VALIDATE_READ(bitStream.Read(languageCode)); return true; } void UIHelpTop5::Handle() { Entity* player = Game::entityManager->GetEntity(objectID); if (!player) { LOG("Unable to get player for UIHelpTop5"); return; } auto sysAddr = player->GetSystemAddress(); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) { LOG("Unable to get system address for player for UIHelpTop5"); return; } AMFArrayValue data; switch (languageCode) { case eLanguageCodeID::EN_US: // Summaries data.Insert("Summary0", Game::config->GetValue("help_0_summary")); data.Insert("Summary1", Game::config->GetValue("help_1_summary")); data.Insert("Summary2", Game::config->GetValue("help_2_summary")); data.Insert("Summary3", Game::config->GetValue("help_3_summary")); data.Insert("Summary4", Game::config->GetValue("help_4_summary")); // Descriptions data.Insert("Description0", Game::config->GetValue("help_0_description")); data.Insert("Description1", Game::config->GetValue("help_1_description")); data.Insert("Description2", Game::config->GetValue("help_2_description")); data.Insert("Description3", Game::config->GetValue("help_3_description")); data.Insert("Description4", Game::config->GetValue("help_4_description")); break; case eLanguageCodeID::PL_US: [[fallthrough]]; case eLanguageCodeID::DE_DE: [[fallthrough]]; case eLanguageCodeID::EN_GB: [[fallthrough]]; default: break; } GameMessages::SendUIMessageServerToSingleClient(player, sysAddr, "UIHelpTop5", data); } bool GeneralChatMessage::Deserialize(RakNet::BitStream& bitStream) { VALIDATE_READ(bitStream.Read(chatChannel)); uint16_t padding; VALIDATE_READ(bitStream.Read(padding)); uint32_t messageLength; VALIDATE_READ(bitStream.Read(messageLength)); for (uint32_t i = 0; i < (messageLength - 1); ++i) { uint16_t character; VALIDATE_READ(bitStream.Read(character)); message.push_back(character); } return true; } void GeneralChatMessage::Handle() { Entity* player = Game::entityManager->GetEntity(objectID); if (!player) return; auto sysAddr = player->GetSystemAddress(); User* user = UserManager::Instance()->GetUser(sysAddr); if (!user) { LOG("Unable to get user to parse chat message"); return; } std::string playerName = user->GetLastUsedChar()->GetName(); bool isMythran = user->GetLastUsedChar()->GetGMLevel() > eGameMasterLevel::CIVILIAN; bool isOk = Game::chatFilter->IsSentenceOkay(GeneralUtils::UTF16ToWTF8(message), user->GetLastUsedChar()->GetGMLevel()).empty(); LOG_DEBUG("Msg: %s was approved previously? %i", GeneralUtils::UTF16ToWTF8(message).c_str(), user->GetLastChatMessageApproved()); if (!isOk) return; if (!isOk && !isMythran) return; std::string sMessage = GeneralUtils::UTF16ToWTF8(message); LOG("%s: %s", playerName.c_str(), sMessage.c_str()); ChatPackets::SendChatMessage(sysAddr, chatChannel, playerName, user->GetLoggedInChar(), isMythran, message); } bool PositionUpdate::Deserialize(RakNet::BitStream& bitStream) { VALIDATE_READ(bitStream.Read(position.x)); VALIDATE_READ(bitStream.Read(position.y)); VALIDATE_READ(bitStream.Read(position.z)); VALIDATE_READ(bitStream.Read(rotation.x)); VALIDATE_READ(bitStream.Read(rotation.y)); VALIDATE_READ(bitStream.Read(rotation.z)); VALIDATE_READ(bitStream.Read(rotation.w)); VALIDATE_READ(bitStream.Read(onGround)); VALIDATE_READ(bitStream.Read(onRail)); bool velocityFlag = false; if (bitStream.Read(velocityFlag) && velocityFlag) { VALIDATE_READ(bitStream.Read(velocity.x)); VALIDATE_READ(bitStream.Read(velocity.y)); VALIDATE_READ(bitStream.Read(velocity.z)); } bool angVelocityFlag = false; if (bitStream.Read(angVelocityFlag) && angVelocityFlag) { VALIDATE_READ(bitStream.Read(angularVelocity.x)); VALIDATE_READ(bitStream.Read(angularVelocity.y)); VALIDATE_READ(bitStream.Read(angularVelocity.z)); } // TODO figure out how to use these. Ignoring for now, but reading in if they exist. bool hasLocalSpaceInfo{}; VALIDATE_READ(bitStream.Read(hasLocalSpaceInfo)); if (hasLocalSpaceInfo) { VALIDATE_READ(bitStream.Read(localSpaceInfo.objectId)); VALIDATE_READ(bitStream.Read(localSpaceInfo.position.x)); VALIDATE_READ(bitStream.Read(localSpaceInfo.position.y)); VALIDATE_READ(bitStream.Read(localSpaceInfo.position.z)); bool hasLinearVelocity = false; if (bitStream.Read(hasLinearVelocity) && hasLinearVelocity) { VALIDATE_READ(bitStream.Read(localSpaceInfo.linearVelocity.x)); VALIDATE_READ(bitStream.Read(localSpaceInfo.linearVelocity.y)); VALIDATE_READ(bitStream.Read(localSpaceInfo.linearVelocity.z)); } } bool hasRemoteInputInfo{}; VALIDATE_READ(bitStream.Read(hasRemoteInputInfo)); if (hasRemoteInputInfo) { VALIDATE_READ(bitStream.Read(remoteInputInfo.m_RemoteInputX)); VALIDATE_READ(bitStream.Read(remoteInputInfo.m_RemoteInputY)); VALIDATE_READ(bitStream.Read(remoteInputInfo.m_IsPowersliding)); VALIDATE_READ(bitStream.Read(remoteInputInfo.m_IsModified)); } return true; } void PositionUpdate::Handle() { Entity* entity = Game::entityManager->GetEntity(objectID); if (entity) entity->ProcessPositionUpdate(*this); } bool StringCheck::Deserialize(RakNet::BitStream& bitStream) { VALIDATE_READ(bitStream.Read(chatMode)); VALIDATE_READ(bitStream.Read(requestID)); for (uint32_t i = 0; i < 42; ++i) { uint16_t character; VALIDATE_READ(bitStream.Read(character)); receiver.push_back(static_cast(character)); } if (!receiver.empty()) { if (std::string(receiver.c_str(), 4) == "[GM]") { // Shift the string forward if we are speaking to a GM as the client appends "[GM]" if they are receiver = std::string(receiver.c_str() + 4, receiver.size() - 4); } } uint32_t messageLength; VALIDATE_READ(bitStream.Read(messageLength)); for (uint32_t i = 0; i < messageLength; ++i) { uint16_t character; VALIDATE_READ(bitStream.Read(character)); message.push_back(static_cast(character)); } return true; } void StringCheck::Handle() { auto* entity = Game::entityManager->GetEntity(objectID); if (!entity) { LOG("Unable to get player to handle string check request"); return; } auto* character = entity->GetCharacter(); if (!character) { LOG("Unable to get character to handle string check request"); return; } auto* user = character->GetParentUser(); if (!user) { LOG("Unable to get user to handle string check request"); return; } // Check if the player has restricted chat access if (character->HasPermission(ePermissionMap::RestrictedChatAccess)) { ChatPackets::SendSystemMessage( entity->GetSystemAddress(), u"This character has restricted chat access." ); return; } bool isBestFriend = false; if (chatMode == eChatMode::UNRESTRICTED) { // Check if the receiver is a best friend LWOOBJID idOfReceiver = LWOOBJID_EMPTY; { auto characterIdFetch = Database::Get()->GetCharacterInfo(receiver); if (characterIdFetch) idOfReceiver = characterIdFetch->id; } const auto& bffMap = user->GetIsBestFriendMap(); if (bffMap.find(receiver) == bffMap.end() && idOfReceiver != LWOOBJID_EMPTY) { auto bffInfo = Database::Get()->GetBestFriendStatus(entity->GetObjectID(), idOfReceiver); if (bffInfo) isBestFriend = bffInfo->bestFriendStatus == 3; if (isBestFriend) user->UpdateBestFriendValue(receiver, true); } else if (bffMap.find(receiver) != bffMap.end()) { isBestFriend = true; } } const auto segments = Game::chatFilter->IsSentenceOkay(message, entity->GetGMLevel(), !(isBestFriend && chatMode == eChatMode::UNRESTRICTED)); bool bAllClean = segments.empty(); if (user->GetIsMuted()) bAllClean = false; user->SetLastChatMessageApproved(bAllClean); ClientPackets::ChatModerationString response; response.receiver = receiver; response.rejectedWords = segments; response.Send(entity->GetSystemAddress()); } }